From 6b9b3bfb9af19e1aa813fcf5c42100c8252056ad Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 13 Aug 2023 23:19:42 -0700 Subject: [PATCH 001/134] Starting on port --- CODE_OF_CONDUCT.md | 133 +++ CONTRIBUTING.md | 13 + Community_Specification_License-v1.md | 99 +++ Governance.md | 51 ++ LICENSE | 13 + Notices.md | 138 ++++ README.md | 1089 +++++++++++++++++++++++++ Scope.md | 5 + 8 files changed, 1541 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Community_Specification_License-v1.md create mode 100644 Governance.md create mode 100644 LICENSE create mode 100644 Notices.md create mode 100644 Scope.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a94f38d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +hello@brooklynzelenka.com, philipp@fission.codes, or hello@fission.codes. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..30dfdd6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +discussion, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure that you have signed the CLA: in a separate PR, add your information to + the [Notices](./Notices.md), and [CLA Bot](./.clabot). +2. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). diff --git a/Community_Specification_License-v1.md b/Community_Specification_License-v1.md new file mode 100644 index 0000000..e49e871 --- /dev/null +++ b/Community_Specification_License-v1.md @@ -0,0 +1,99 @@ +# Community Specification License 1.0 + +**The Purpose of this License.** This License sets forth the terms under which 1) Contributor will participate in and contribute to the development of specifications, standards, best practices, guidelines, and other similar materials under this Working Group, and 2) how the materials developed under this License may be used. It is not intended for source code. Capitalized terms are defined in the License’s last section. + +**1. Copyright.** + +**1.1. Copyright License.** Contributor grants everyone a non-sublicensable, perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as expressly stated in this License) copyright license, without any obligation for accounting, to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute any materials it submits to the full extent of its copyright interest in those materials. Contributor also acknowledges that the Working Group may exercise copyright rights in the Specification, including the rights to submit the Specification to another standards organization. + +**1.2. Copyright Attribution.** As a condition, anyone exercising this copyright license must include attribution to the Working Group in any derivative work based on materials developed by the Working Group. That attribution must include, at minimum, the material’s name, version number, and source from where the materials were retrieved. Attribution is not required for implementations of the Specification. + +**2. Patents.** + +**2.1. Patent License.** + +**2.1.1. As a Result of Contributions.** + +**2.1.1.1. As a Result of Contributions to Draft Specifications.** Contributor grants Licensee a non-sublicensable, perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as expressly stated in this License) license to its Necessary Claims in 1) Contributor’s Contributions and 2) to the Draft Specification that is within Scope as of the date of that Contribution, in both cases for Licensee’s Implementation of the Draft Specification, except for those patent claims excluded by Contributor under Section 3. + +**2.1.1.2. For Approved Specifications.** Contributor grants Licensee a non-sublicensable, perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as expressly stated in this License) license to its Necessary Claims included the Approved Specification that are within Scope for Licensee’s Implementation of the Approved Specification, except for those patent claims excluded by Contributor under Section 3. + +**2.1.2. Patent Grant from Licensee.** Licensee grants each other Licensee a non-sublicensable, perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as expressly stated in this License) license to its Necessary Claims for its Implementation, except for those patent claims excluded under Section 3. + +**2.1.3. Licensee Acceptance.** The patent grants set forth in Section 2.1 extend only to Licensees that have indicated their agreement to this License as follows: + +**2.1.3.1. Source Code Distributions.** For distribution in source code, by including this License in the root directory of the source code with the Implementation; + +**2.1.3.2. Non-Source Code Distributions.** For distribution in any form other than source code, by including this License in the documentation, legal notices, via notice in the software, and/or other written materials provided with the Implementation; or + +**2.1.3.3. Via Notices.md.** By issuing pull request or commit to the Specification’s repository’s Notices.md file by the Implementer’s authorized representative, including the Implementer’s name, authorized individual and system identifier, and Specification version. + +**2.1.4. Defensive Termination.** If any Licensee files or maintains a claim in a court asserting that a Necessary Claim is infringed by an Implementation, any licenses granted under this License to the Licensee are immediately terminated unless 1) that claim is directly in response to a claim against Licensee regarding an Implementation, or 2) that claim was brought to enforce the terms of this License, including intervention in a third-party action by a Licensee. + +**2.1.5. Additional Conditions.** This License is not an assurance (i) that any of Contributor’s copyrights or issued patent claims cover an Implementation of the Specification or are enforceable or (ii) that an Implementation of the Specification would not infringe intellectual property rights of any third party. + +**2.2. Patent Licensing Commitment.** In addition to the rights granted in Section 2.1, Contributor agrees to grant everyone a no charge, royalty-free license on reasonable and non-discriminatory terms to Contributor’s Necessary Claims that are within Scope for: +1) Implementations of a Draft Specification, where such license applies only to those Necessary Claims infringed by implementing Contributor's Contribution(s) included in that Draft Specification, and +2) Implementations of the Approved Specification. + +This patent licensing commitment does not apply to those claims subject to Contributor’s Exclusion Notice under Section 3. + +**2.3. Effect of Withdrawal.** Contributor may withdraw from the Working Group by issuing a pull request or commit providing notice of withdrawal to the Working Group repository’s Notices.md file. All of Contributor’s existing commitments and obligations with respect to the Working Group up to the date of that withdrawal notice will remain in effect, but no new obligations will be incurred. + +**2.4. Binding Encumbrance.** This License is binding on any future owner, assignee, or party who has been given the right to enforce any Necessary Claims against third parties. + +**3. Patent Exclusion.** + +**3.1. As a Result of Contributions.** Contributor may exclude Necessary Claims from its licensing commitments incurred under Section 2.1.1 by issuing an Exclusion Notice within 45 days of the date of that Contribution. Contributor may not issue an Exclusion Notice for any material that has been included in a Draft Deliverable for more than 45 days prior to the date of that Contribution. + +**3.2. As a Result of a Draft Specification Becoming an Approved Specification.** Prior to the adoption of a Draft Specification as an Approved Specification, Contributor may exclude Necessary Claims from its licensing commitments under this Agreement by issuing an Exclusion Notice. Contributor may not issue an Exclusion Notice for patents that were eligible to have been excluded pursuant to Section 3.1. + +**4. Source Code License.** Any source code developed by the Working Group is solely subject the source code license included in the Working Group’s repository for that code. If no source code license is included, the source code will be subject to the MIT License. + +**5. No Other Rights.** Except as specifically set forth in this License, no other express or implied patent, trademark, copyright, or other rights are granted under this License, including by implication, waiver, or estoppel. + +**6. Antitrust Compliance.** Contributor acknowledge that it may compete with other participants in various lines of business and that it is therefore imperative that they and their respective representatives act in a manner that does not violate any applicable antitrust laws and regulations. This License does not restrict any Contributor from engaging in similar specification development projects. Each Contributor may design, develop, manufacture, acquire or market competitive deliverables, products, and services, and conduct its business, in whatever way it chooses. No Contributor is obligated to announce or market any products or services. Without limiting the generality of the foregoing, the Contributors agree not to have any discussion relating to any product pricing, methods or channels of product distribution, division of markets, allocation of customers or any other topic that should not be discussed among competitors under the auspices of the Working Group. + +**7. Non-Circumvention.** Contributor agrees that it will not intentionally take or willfully assist any third party to take any action for the purpose of circumventing any obligations under this License. + +**8. Representations, Warranties and Disclaimers.** + +**8.1. Representations, Warranties and Disclaimers.** Contributor and Licensee represents and warrants that 1) it is legally entitled to grant the rights set forth in this License and 2) it will not intentionally include any third party materials in any Contribution unless those materials are available under terms that do not conflict with this License. IN ALL OTHER RESPECTS ITS CONTRIBUTIONS ARE PROVIDED "AS IS." The entire risk as to implementing or otherwise using the Contribution or the Specification is assumed by the implementer and user. Except as stated herein, CONTRIBUTOR AND LICENSEE EXPRESSLY DISCLAIM ANY WARRANTIES (EXPRESS, IMPLIED, OR OTHERWISE), INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, CONDITIONS OF QUALITY, OR TITLE, RELATED TO THE CONTRIBUTION OR THE SPECIFICATION. IN NO EVENT WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any obligations regarding the transfer, successors in interest, or assignment of Necessary Claims will be satisfied if Contributor or Licensee notifies the transferee or assignee of any patent that it knows contains Necessary Claims or necessary claims under this License. Nothing in this License requires Contributor to undertake a patent search. If Contributor is 1) employed by or acting on behalf of an employer, 2) is making a Contribution under the direction or control of a third party, or 3) is making the Contribution as a consultant, contractor, or under another similar relationship with a third party, Contributor represents that they have been authorized by that party to enter into this License on its behalf. + +**8.2. Distribution Disclaimer.** Any distributions of technical information to third parties must include a notice materially similar to the following: “THESE MATERIALS ARE PROVIDED “AS IS.” The Contributors and Licensees expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the materials. The entire risk as to implementing or otherwise using the materials is assumed by the implementer and user. IN NO EVENT WILL THE CONTRIBUTORS OR LICENSEES BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS DELIVERABLE OR ITS GOVERNING AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER MEMBER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.” + +**9. Definitions.** + +**9.1. Affiliate.** “Affiliate” means an entity that directly or indirectly Controls, is Controlled by, or is under common Control of that party. + +**9.2. Approved Specification.** “Approved Specification” means the final version and contents of any Draft Specification designated as an Approved Specification as set forth in the accompanying Governance.md file. + +**9.3. Contribution.** “Contribution” means any original work of authorship, including any modifications or additions to an existing work, that Contributor submits for inclusion in a Draft Specification, which is included in a Draft Specification or Approved Specification. + +**9.4. Contributor.** “Contributor” means any person or entity that has indicated its acceptance of the License 1) by making a Contribution to the Specification, or 2) by entering into the Community Specification Contributor License Agreement for the Specification. Contributor includes its Affiliates, assigns, agents, and successors in interest. + +**9.5. Control.** “Control” means direct or indirect control of more than 50% of the voting power to elect directors of that corporation, or for any other entity, the power to direct management of such entity. + +**9.6. Draft Specification.** “Draft Specification” means all versions of the material (except an Approved Specification) developed by this Working Group for the purpose of creating, commenting on, revising, updating, modifying, or adding to any document that is to be considered for inclusion in the Approved Specification. + +**9.7. Exclusion Notice.** “Exclusion Notice” means a written notice made by making a pull request or commit to the repository’s Notices.md file that identifies patents that Contributor is excluding from its patent licensing commitments under this License. The Exclusion Notice for issued patents and published applications must include the Draft Specification’s name, patent number(s) or title and application number(s), as the case may be, for each of the issued patent(s) or pending patent application(s) that the Contributor is excluding from the royalty-free licensing commitment set forth in this License. If an issued patent or pending patent application that may contain Necessary Claims is not set forth in the Exclusion Notice, those Necessary Claims shall continue to be subject to the licensing commitments under this License. The Exclusion Notice for unpublished patent applications must provide either: (i) the text of the filed application; or (ii) identification of the specific part(s) of the Draft Specification whose implementation makes the excluded claim a Necessary Claim. If (ii) is chosen, the effect of the exclusion will be limited to the identified part(s) of the Draft Specification. + +**9.8. Implementation.** “Implementation” means making, using, selling, offering for sale, importing or distributing any implementation of the Specification 1) only to the extent it implements the Specification and 2) so long as all required portions of the Specification are implemented. + +**9.9. License.** “License” means this Community Specification License. + +**9.10. Licensee.** “Licensee” means any person or entity that has indicated its acceptance of the License as set forth in Section 2.1.3. Licensee includes its Affiliates, assigns, agents, and successors in interest. + +**9.11. Necessary Claims.** “Necessary Claims” are those patent claims, if any, that a party owns or controls, including those claims later acquired, that are necessary to implement the required portions (including the required elements of optional portions) of the Specification that are described in detail and not merely referenced in the Specification. + +**9.12. Specification.** “Specification” means a Draft Specification or Approved Specification included in the Working Group’s repository subject to this License, and the version of the Specification implemented by the Licensee. + +**9.13. Scope.** “Scope” has the meaning as set forth in the accompanying Scope.md file included in this Specification’s repository. Changes to Scope do not apply retroactively. If no Scope is provided, each Contributor’s Necessary Claims are limited to that Contributor’s Contributions. + +**9.14. Working Group.** “Working Group” means this project to develop specifications, standards, best practices, guidelines, and other similar materials under this License. + + + +*The text of this Community Specification License is Copyright 2020 Joint Development Foundation and is licensed under the Creative Commons Attribution 4.0 International License available at https://creativecommons.org/licenses/by/4.0/.* + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/Governance.md b/Governance.md new file mode 100644 index 0000000..b36de21 --- /dev/null +++ b/Governance.md @@ -0,0 +1,51 @@ +# Community Specification Governance Policy 1.0 + +This document provides the governance policy for specifications and other documents developed using the Community Specification process in a repository (each a “Working Group”). Each Working Group and must adhere to the requirements in this document. + +## 1. Roles. + +Each Working Group may include the following roles. Additional roles may be adopted and documented by the Working Group. + +**1.1. Maintainer.** “Maintainers” are responsible for organizing activities around developing, maintaining, and updating the specification(s) developed by the Working Group. Maintainers are also responsible for determining consensus and coordinating appeals. Each Working Group will designate one or more Maintainer for that Working Group. A Working Group may select a new or additional Maintainer(s) upon Approval of the Working Group Participants. + +**1.2. Editor.** “Editors” are responsible for ensuring that the contents of the document accurately reflect the decisions that have been made by the group, and that the specification adheres to formatting and content guidelines. Each Working Group will designate an Editor for that Working Group. A Working Group may select a new Editor upon Approval of the Working Group Participants. + +**1.3. Participants.** “Participants” are those that have made Contributions to the Working Group subject to the Community Specification License. + +## 2. Decision Making. + +**2.1. Consensus-Based Decision Making.** Working Groups make decisions through a consensus process (“Approval” or “Approved”). While the agreement of all Participants is preferred, it is not required for consensus. Rather, the Maintainer will determine consensus based on their good faith consideration of a number of factors, including the dominant view of the Working Group Participants and nature of support and objections. The Maintainer will document evidence of consensus in accordance with these requirements. + +**2.2. Appeal Process.** Decisions may be appealed be via a pull request or an issue, and that appeal will be considered by the Maintainer in good faith, who will respond in writing within a reasonable time. + +## 3. Ways of Working. + +Inspired by [ANSI’s Essential Requirements for Due Process](https://share.ansi.org/Shared%20Documents/Standards%20Activities/American%20National%20Standards/Procedures,%20Guides,%20and%20Forms/2020_ANSI_Essential_Requirements.pdf), Community Specification Working Groups must adhere to consensus-based due process requirements. These requirements apply to activities related to the development of consensus for approval, revision, reaffirmation, and withdrawal of Community Specifications. Due process means that any person (organization, company, government agency, individual, etc.) with a direct and material interest has a right to participate by: a) expressing a position and its basis, b) having that position considered, and c) having the right to appeal. Due process allows for equity and fair play. The following constitute the minimum acceptable due process requirements for the development of consensus. + +**3.1. Openness.** Participation shall be open to all persons who are directly and materially affected by the activity in question. There shall be no undue financial barriers to participation. Voting membership on the consensus body shall not be conditional upon membership in any organization, nor unreasonably restricted on the basis of technical qualifications or other such requirements. Membership in a Working Group’s parent organization, if any, may be required. + +**3.2. Lack of Dominance.** The development process shall not be dominated by any single interest category, individual or organization. Dominance means a position or exercise of dominant authority, leadership, or influence by reason of superior leverage, strength, or representation to the exclusion of fair and equitable consideration of other viewpoints. + +**3.3. Balance.** The development process should have a balance of interests. Participants from diverse interest categories shall be sought with the objective of achieving balance. + +**3.4. Coordination and Harmonization.** Good faith efforts shall be made to resolve potential conflicts between and among deliverables developed under this Working Group and existing industry standards. + +**3.5. Consideration of Views and Objections.** Prompt consideration shall be given to the written views and objections of all Participants. + +**3.6. Written procedures.** This governance document and other materials documenting the Community Specification development process shall be available to any interested person. + +## 4. Specification Development Process. + +**4.1. Pre-Draft.** Any Participant may submit a proposed initial draft document as a candidate Draft Specification of that Working Group. The Maintainer will designate each submission as a “Pre-Draft” document. + +**4.2. Draft.** Each Pre-Draft document of a Working Group must first be Approved to become a” Draft Specification”. Once the Working Group approves a document as a Draft Specification, the Draft Specification becomes the basis for all going forward work on that specification. + +**4.3. Working Group Approval.** Once a Working Group believes it has achieved the objectives for its specification as described in the Scope, it will Approve that Draft Specification and progress it to “Approved Specification” status. + +**4.4. Publication and Submission.** Upon the designation of a Draft Specification as an Approved Specification, the Maintainer will publish the Approved Specification in a manner agreed upon by the Working Group Participants (i.e., Working Group Participant only location, publicly available location, Working Group maintained website, Working Group member website, etc.). The publication of an Approved Specification in a publicly accessible manner must include the terms under which the Approved Specification is being made available under. + +**4.5. Submissions to Standards Bodies.** No Draft Specification or Approved Specification may be submitted to another standards development organization without Working group Approval. Upon reaching Approval, the Maintainer will coordinate the submission of the applicable Draft Specification or Approved Specification to another standards development organization. Working Group Participants that developed that Draft Specification or Approved Specification agree to grant the copyright rights necessary to make those submissions. + +## 5. Non-Confidential, Restricted Disclosure. + +Information disclosed in connection with any Working Group activity, including but not limited to meetings, Contributions, and submissions, is not confidential, regardless of any markings or statements to the contrary. Notwithstanding the foregoing, if the Working Group is collaborating via a private repository, the Participants will not make any public disclosures of that information contained in that private repository without the Approval of the Working Group. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c7d1e9f --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +# Licenses + +## Specification License + +Specifications in the repository are subject to the **Community Specification License 1.0** available at [https://github.com/CommunitySpecification/1.0](https://github.com/CommunitySpecification/1.0). + +## Source Code License + +If source code is included in this repository, or for sample or reference code included in the specification itself, that code is subject to the MIT license unless otherwise designated. In the case of any conflict or confusion within this specification repository between the Community Specification License and the MIT or other designated license, the terms of the Community Specification License shall apply. + +If source code is included in this repository, or for sample or reference code included in the specification itself, that code is subject to the MIT license unless otherwise marked. + +In the case of any conflict or confusion within this specification repository between the Community Specification License and the designated source code license, the terms of the Community Specification License shall apply. diff --git a/Notices.md b/Notices.md new file mode 100644 index 0000000..28a77da --- /dev/null +++ b/Notices.md @@ -0,0 +1,138 @@ +# Notices + +## Code of Conduct + +The Code of Conduct is available in the repository in [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md). + +Contact for Code of Conduct issues or inquires: hello@brooklynzelenka.com, hello@fission.codes + +## License Acceptance + +Per Community Specification License 1.0 Section 2.1.3.3, Licensees may indicate their acceptance of the Community Specification License by issuing a pull request to the Specification’s repository’s Notice.md file, including the Licensee’s name, authorized individuals' names, and repository system identifier (e.g. GitHub ID), and specification version. + +A Licensee may consent to accepting the current Community Specification License version or any future version of the Community Specification License by indicating "or later" after their specification version. + +--------------------------------------------------------------------------------- + +Licensee’s name: Brooklyn Zelenka + +Authorized individual and system identifier: expede + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Brian Ginsburg + +Authorized individual and system identifier: bgins + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Stephen Akinyemi + +Authorized individual and system identifier: appcypher + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Quinn Wilton + +Authorized individual and system identifier: QuinnWilton + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Philipp Krüger + +Authorized individual and system identifier: matheus23 + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + + +Licensee’s name: Boris Mann + +Authorized individual and system identifier: bmann + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Daniel Holmgren + +Authorized individual and system identifier: dholms + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Irakli Gozalishvili + +Authorized individual and system identifier: gozala + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Brendan O'Brien + +Authorized individual and system identifier: b5 + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Benjamin Goering + +Authorized individual and system identifier: gobengo + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +Licensee’s name: Nicolas Gimenez + +Authorized individual and system identifier: nicobao + +Specification version: 0.8.0 or later + +--------------------------------------------------------------------------------- + +## Withdrawals + +Name of party withdrawing: + +Date of withdrawal: + +--------------------------------------------------------------------------------- + +## Exclusions + +This section includes any Exclusion Notices made against a Draft Deliverable or Approved Deliverable as set forth in the Community Specification Development License. Each Exclusion Notice must include the following information: + +- Name of party making the Exclusion Notice: + +- Name of patent owner: + +- Specification: + +- Version number: + +**For issued patents and published patent applications:** + + (i) patent number(s) or title and application number(s), as the case may be: + + (ii) identification of the specific part(s) of the Specification whose implementation makes the excluded claim a Necessary Claim. + +**For unpublished patent applications must provide either:** + + (i) the text of the filed application; or + + (ii) identification of the specific part(s) of the Specification whose implementation makes the excluded claim a Necessary Claim. + +----------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index e69de29..bcd66fe 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,1089 @@ +# User Controlled Authorization Network (UCAN) Specification v0.10.0 + +## Editors + +* [Brooklyn Zelenka], [Fission] + +## Authors + +* [Brooklyn Zelenka], [Fission] +* [Daniel Holmgren], [Bluesky] +* [Irakli Gozalishvili], [Protocol Labs] +* [Philipp Krüger], [Fission] + +# 0. Abstract + +User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first, user-originated authorization and revocation scheme. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. UCAN allows for the creation and discharge of authority by any agent with a DID, including traditional systems and peer-to-peer architectures beyond traditional cloud computing. + +## Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. + +# 1. Introduction + +## 1.1 Motivation + +Since at least the release of Unix, access control lists (ACLs) have been the most popular form of digital authorization, where a list of what each user is allowed to do is maintained on the resource. ACLs have been a successful model suited to architectures where persistent access to a single list is viable. ACLs require that rules are sufficiently well specified, such as in a centralized database with rules covering all possible permutations of rights. + +With increasing interconnectivity between machines becoming commonplace, authorization needs to scale to meet the load demands of distributed systems while providing partition tolerance. However, it is not always practical to maintain a single central authorization source. Even when copies of the authorization list are distributed to the relevant servers, latency and partitions introduce troublesome challenges with conflicting updates, to say nothing of storage requirements. + +A large portion of personal information now also moves through connected systems. As a result, data privacy is a prominent theme when considering the design of modern applications, to the point of being legislated in parts of the world. + +Ahead-of-time coordination is often a barrier to development in many projects. Flexibility to define specialized authorization semantics for resources and the ability to integrate with external systems trustlessly are essential as the number of autonomous, specialized, and coordinating applications increases. + +Many high-value applications run in hostile environments. In recognition of this, many vendors now include public key functionality, such as [non-extractable keys in browsers][browser api crypto key], [certificate systems for external keys][fido], and [secure hardware enclave]s in widespread consumer devices. + +Two related models that work exceptionally well in the above context are Simple Public Key Infrastructure ([SPKI][spki rfc]) and object capabilities ([OCAP]). Since offline operation and self-verifiability are two requirements, UCAN adopts an approach closely related to SPKI. UCANs follow the "capabilities as certificates" model, with extensions for revocation and stateful capabilities. + +## 1.2 Intuition + +By analogy, ACLs are like a bouncer at an exclusive event. This bouncer has a list attendees allowed in and which of those are VIPs that get extra access. The attendees show their government-issued ID and are accepted or rejected. In addition, they may get a lanyard to identify that they have previously been allowed in. If someone is disruptive, they can simply be crossed off the list and denied further entry. + +If there are many such events at many venues, the organizers need to coordinate ahead of time, denials need to be synchronized, and attendees need to show their ID cards to many bouncers. The likelihood of the bouncer letting in the wrong person due to synchronization lag or confusion by someone sharing a name is nonzero. + +UCANs work more like [movie tickets][caps as keys] or a festival pass between multiple venues. No one needs to check your ID; who you are is irrelevant. For example, if you have a ticket to see Citizen Kane, you are admitted to Theater 3. If you cannot attend an event, you can hand this ticket to a friend who wants to see the film instead, and there is no coordination required with the theater ahead of time. However, if the theater needs to cancel tickets for some reason, they need a way of uniquely identifying them and sharing this information between them. + +The above analogies illustrate several significant tradeoffs between these systems but are only accurate enough to build intuition. A good resource for a more thorough presentation of these tradeoffs is [Capability Myths Demolished]. In this framework, UCAN approximates SPKI with some dynamic features. + +## 1.3 Security Considerations + +Each UCAN includes a constructive set of assertions of what it is allowed to do. Note that this is not a predicate: it is a positive assertion of rights. "Proofs" are positive evidence (elsewhere called "witnesses") of the possession of rights. They are cryptographically signed data showing that the UCAN issuer either owns this or that it was delegated to them by the root owner. + +This signature chain is the root of trust. Private keys themselves SHOULD NOT move from one context to another: this is what the delegation mechanism provides: "sharing authority without sharing keys." + +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. + +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. + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ │ │ │ │ │ +│ │ │ ┌─────────┐ │ │ │ +│ │ │ │ Bob's │ │ │ │ +│ │ │ │ Photo │ │ │ │ +│ │ │ │ Gallery │ │ │ │ +│ │ │ └─────────┘ │ │ │ +│ │ │ │ │ │ +│ Alice's │ │ Bob's │ │ Carol's │ +│ Stuff │ │ Stuff │ │ Stuff │ +│ │ │ │ │ │ +│ ┌───────┼───┼─────────────┼───┼──┐ │ +│ │ │ │ │ │ │ │ +│ │ │ │ ┌───┼───┼──┼────────┐ │ +│ │ │ │ Alice's │ │ │ │ │ │ +│ │ │ │ Music │ │ │ │Carol's │ │ +│ │ │ │ Player │ │ │ │ Game │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ │ └───┼───┼──┼────────┘ │ +│ │ │ │ │ │ │ │ +│ └───────┼───┼─────────────┼───┼──┘ │ +│ │ │ │ │ │ +└─────────────┘ └─────────────┘ └─────────────┘ +``` + +# 2. Terminology + +## 2.1 Roles + +There are several roles that an agent MAY assume: + +| Name | Description | +| --------- | ----------- | +| Agent | The general class of entities and principals that interact with a UCAN | +| Validator | Any agent that interprets a UCAN to determine that it is valid, and which capabilities it grants | +| Principal | An agent identified by DID (listed in a UCAN's `iss` or `aud` field) | +| Audience | The principal delegated to in the current UCAN. Listed in the `aud` field | +| Signer | A principal that can sign payloads | +| Issuer | The signer of the current UCAN. Listed in the `iss` field | +| Revoker | The issuer listed in a proof chain that revokes a UCAN | +| Owner | The root issuer of a capability, who has some proof that they fully control the resource | + +``` +┌────────────────────────────────────────────────────────────────────────────────────────────┐ +│ │ +│ Agent │ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ ┌──────────────────────┐ │ +│ │ │ │ │ │ +│ │ Principal │ │ Validator │ │ +│ │ │ │ │ │ +│ │ ┌──────────────────────────┐ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ Signer │ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ │ ┌────────────────────┐ │ ┌──────────────────────┐ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ Issuer │ │ │ Audience │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ │ Owner │ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ │ Revoker │ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ └────────────────────┘ │ └──────────────────────┘ │ │ │ │ +│ │ │ │ │ │ │ │ +│ │ └──────────────────────────┘ │ │ │ │ +│ │ │ │ │ │ +│ └──────────────────────────────────────────────────────────┘ └──────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────────────────────────┘ +``` + +## 2.2 Resource + +A resource is some data or process that has an address. It can be anything from a row in a database, a user account, storage quota, email address, etc. + +A resource describes the noun of a capability. The resource pointer MUST be provided in [URI] format. Arbitrary and custom URIs MAY be used, provided that the intended recipient can decode the URI. The URI is merely a unique identifier to describe the pointer to — and within — a resource. + +The same resource MAY be addressed with several URI formats. For instance, a database may have an address at the level of direct memory with `file`, via `sqldb` to gain access to SQL semantics, `http` to use web addressing, and `dnslink` to use Merkle DAGs inside DNS `TXT` records. + +## 2.3 Ability + +Abilities describe the verb portion of the capability: an ability that can be performed on a resource. For instance, the standard HTTP methods such as `GET`, `PUT`, and `POST` would be possible `can` values for an `http` resource. While arbitrary semantics MAY be described, they MUST apply to the target resource. For instance, it does not make sense to apply `msg/send` to a typical file system. + +Abilities MAY be organized in a hierarchy with enums. 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 potencies this way allows for adding more options over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. + +Abilities MUST NOT be case sensitive. For example, `http/post`, `http/POST`, `HTTP/post`, `HTTP/POST`, and `hTtP/pOsT` MUST all mean the same ability. + +There MUST be at least one path segment as a namespace. For example, `http/put` and `db/put` MUST be treated as unique from each other. + +The only reserved ability MUST be the un-namespaced [`"*"` (top)][top ability], which MUST be allowed on any resource. + +## 2.4 Caveats + +Capabilities MAY define additional optional or required fields specific to their use case in the caveat fields. This field is OPTIONAL in the general case, but MAY be REQUIRED by particular capability types that require this information to validate. Caveats MAY function as an "escape hatch" for when a use case is not fully captured by the resource and ability fields. Caveats can be read as "on the condition that `` holds". + +## 2.5 Capability + +A capability is the association of an "ability" to a "resource": `resource x ability x caveats`. + +The resource and ability fields are REQUIRED. Any non-normative extensions are OPTIONAL. + +``` +{ $RESOURCE: { $ABILITY: [ $CAVEATS ] } } +``` + +### 2.5.1 Examples + +``` json +{ + "example://example.com/public/photos/": { + "crud/read": [{}], + "crud/delete": [ + { + "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" + } + ] + }, + "example://example.com/private/84MZ7aqwKn7sNiMGsSbaxsEa6EPnQLoKYbXByxNBrCEr": { + "wnfs/append": [{}] + }, + "mailto:username@example.com": { + "msg/send": [{}], + "msg/receive": [ + { + "max_count": 5, + "templates": [ + "newsletter", + "marketing" + ] + } + ] + } +} +``` + +## 2.6 Authority + +The set of capabilities delegated by a UCAN is called its "authority." This functions as a declarative description of delegated abilities. + +Merging capability authorities MUST follow set semantics, where the result includes all capabilities from the input authorities. Since broader capabilities automatically include narrower ones, this process is always additive. Capability authorities can be combined in any order, with the result always being at least as broad as each of the original authorities. + +``` plaintext + ┌───────────────────┐ ─┐ + │ │ │ + │ │ │ +┌────────────────┼───┐ │ │ +│ │ │ Resource B │ │ +│ │ │ │ │ BxZ +│ │ │ X │ ├─── Capability +│ Resource A │ │ │ │ +│ │ │ Ability Z │ │ +│ X │ │ │ │ +│ │ │ │ │ +│ Ability Y │ │ │ │ +│ └───┼───────────────┘ ─┘ +│ │ +│ │ +└────────────────────┘ + +└──────────────────┬─────────────────┘ + │ + + AxY U BxZ + authority +``` + +The capability authority is the total rights of the authorization space down to the relevant volume of authorizations. Individual capabilities MAY overlap; the authority is the union. Except for [rights amplification], every unique delegation MUST have equal or narrower capabilities from their delegator. Inside this content space, you can draw a boundary around some resource(s) (their type, identifiers, and paths or children) and their capabilities. + +For example, given the following authorities against a WebNative filesystem, they can be merged as follows: + +```js +// "wnfs" abilities: +// fetch < append < overwrite < superuser + +AuthorityA = { + "wnfs://alice.example.com/pictures/": { + "wnfs/append": [{}] + } +} + +AuthorityB = { + "wnfs://alice.example.com/pictures/vacation/": { + "wnfs/append": [{}] + }, + "wnfs://alice.example.com/pictures/vacation/hawaii/": { + "wnfs/overwrite": [{}] + } +} + +merge(AuthorityA, AuthorityB) == { + "wnfs://alice.example.com/pictures/": { + "wnfs/append": [{}], + }, + "wnfs://alice.example.com/pictures/vacation/hawaii": { + "wnfs/overwrite": [{}] + } + // Note that ("/pictures/vacation/" x append) has become redundant, being contained in ("/pictures/" x append) +} +``` + +## 2.7 Delegation + +Delegation is the act of granting another principal (the delegate) the capability to use a resource that another has (the delegator). A constructive "proof" acts as the authorization proof for a delegation. Proofs are either the owning principal's signature or a UCAN with access to that capability in its authority. + +Each direct delegation leaves the ability at the same level or diminishes it. The only exception is in "rights amplification," where a delegation MAY be proven by one-or-more proofs of different types if part of the resource's semantics. + +Note that delegation is a separate concept from [invocation]. Delegation is the act of granting a capability to another, not the act of using it (invocation), which has additional requirements. + +## 2.8 Attenuation + +Attenuation is the process of constraining the capabilities in a delegation chain. + + +### 2.8.1 Examples + +``` json +// Example claimed capabilities + +{ + "example://example.com/public/photos/": { + "crud/read": [{}], + "crud/delete": [ + { + "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" + } + ] + }, + "mailto:username@example.com": { + "msg/send": [{}], + "msg/receive": [ + { + "max_count": 5, + "templates": [ + "newsletter", + "marketing" + ] + } + ] + } +} + + +// Example proof capabilities + +{ + "example://example.com/public/photos/": { + "crud/read": [{}], + "crud/delete": [{}], // Proof is (correctly) broader than claimed + }, + "mailto:username@example.com": { + "msg/send": [{}], // Proof is (correctly) broader than claimed + "msg/receive": [ + { + "max_count": 5, + "templates": [ + "newsletter", + "marketing" + ] + } + ] + }, + "dns:example.com": { // Not delegated, so no problem + "crud/create": [ + {"type": "A"}, + {"type": "CNAME"}, + {"type": "TXT"} + ] + } +} +``` + +## 2.9 Revocation + +Revocation is the act of invalidating a UCAN after the fact, outside of the limitations placed on it by the UCAN's fields (such as its expiry). + +In the case of UCAN, this MUST be done by a proof's issuer DID. For more on the exact mechanism, see the revocation validation section. + +## 2.10 Invocation + +UCANs are used to delegate capabilities between DID-holding agents, eventually terminating in an "invocation" of those capabilities. Invocation is when the capability is exercised to perform some task on a resource. Invocation has its [own specification][invocation]. + +## 2.11 Time + +Time takes on [multiple meanings][time definition] in systems representing facts or knowledge. The senses of the word "time" are given below. + +### 2.11.1 Valid Time Range + +The period of time that a capability is valid from and until. + +### 2.11.2 Assertion Time + +The moment at which a delegation was asserted. This MAY be captured via an `iat` field, but is generally superfluous to capture in the token. "Assertion time" is useful when discussing the lifecycle of a token. + +### 2.11.3 Decision (or Validation) Time + +Decision time is the part of the lifecycle when "a decision" about the token is made. This is typically during validation, but also includes resolving external state (e.g. storage quotas). + +# 3. JWT Structure + +UCANs MUST be formatted as JWTs, with additional keys as described in this document. The overall container of a header, claims, and signature remains. Please refer to [RFC 7519][JWT] for more on this format. + +## 3.1 Header + +The header MUST include all of the following fields: +| Field | Type | Description | Required | +|-------|----------|--------------------------------|----------| +| `alg` | `String` | Signature algorithm | Yes | +| `typ` | `String` | Type (MUST be `"JWT"`) | Yes | + +The header is a standard JWT header. + +EdDSA, as applied to JOSE (including JWT), is described in [RFC 8037]. + +Note that the JWT `"alg": "none"` option MUST NOT be supported. The lack of signature prevents the issuer from being validatable. + +### Examples + +```json +{ + "alg": "EdDSA", + "typ": "JWT", +} +``` + +## 3.2 Payload + +The payload MUST describe the authorization claims, who is involved, and its validity period. + +| Field | Type | Description | Required | +|-------|------------------------------|---------------------------------------------|----------| +| `ucv` | `String` | UCAN Semantic Version (`0.2.0`) | Yes | +| `iss` | `String` | Issuer DID (sender) | Yes | +| `aud` | `String` | Audience DID (receiver) | Yes | +| `nbf` | `Integer` | Not Before UTC Unix Timestamp (valid from) | No | +| `exp` | `Integer \| null` | Expiration UTC Unix Timestamp (valid until) | Yes | +| `nnc` | `String` | Nonce | No | +| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | +| `cap` | `{URI: {Ability: [Object]}}` | Capabilities | Yes | +| `prf` | `[CID]` | Proof of delegation (hash-linked UCANs) | No | + + +### 3.2.1 Version + +The `ucv` field sets the version of the UCAN specification used in the payload. + +### 3.2.2 Principals + +The `iss` and `aud` fields describe the token's principals. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. + +The `iss` and `aud` fields MUST contain a single principal each. + +If an issuer's DID has more than one key (e.g. [`did:ion`], [`did:3`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. + +It is RECOMMENDED that the underlying key types RSA, ECDSA, and EdDSA be supported. + +#### Examples + +```json +"aud": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", +"iss": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169", +``` + +```json +"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", +"iss": "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", +``` + +```json +"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", +"iss": "did:ion:test:EiANCLg1uCmxUR4IUkpW8Y5_nuuXLbAEwonQd4q8pflTnw#key-1", +``` + +```json +"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", +"iss": "did:3:bafyreiffkeeq4wq2htejqla2is5ognligi4lvjhwrpqpl2kazjdoecmugi#yh27jTt7Ny2Pwdy", +``` + +### 3.2.3 Time Bounds + +`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. + +The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. + +The `exp` field MUST be set. Following the [principle of least authority][POLA], it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid. + +Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. + +#### Examples + +```json +"nbf": 1529496683, +"exp": 1575606941, +``` + +### 3.2.4 Nonce + +The OPTIONAL nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. + +This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the `fct` field for more. + +#### Examples + +``` json +"nnc": "1701-D" +``` + +### 3.2.5 Facts + +The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. + +#### Examples + +``` json +{ + "fct": { + "challenges": { + "example.com": "abcdef", + "another.example.net": "12345" + }, + "sha3_256": { + "B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9": "hello world" + } + } +} +``` + +### 3.2.6 Capabilities & Attenuation + +Capabilities MUST be presented as a map. This map is REQUIRED but MAY be empty. + +This map MUST contain some or none of the following: +1. A strict subset (attenuation) of the capability authority from the `prf` field +2. Capabilities composed from multiple proofs (see [rights amplification]) +3. Capabilities originated by the `iss` DID (i.e. by parenthood) + +The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. + +``` +{ $RESOURCE: { $ABILITY: [ ...$CAVEATS ] } } +``` + +#### 3.2.6.1 Resource + +Resources MUST be unique and given as [URI]s. + +Resources in the capabilities map MAY overlap. For example, the following MAY coexist as top-level keys in a capabilities map: + +```json +"https://example.com", +"https://example.com/blog" +``` + +#### 3.2.6.2 Abilities + +Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. + +#### 3.2.6.3 Caveat Array + +Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST prevent invocation otherwise. Caveats MUST be formatted as objects. + +On validation, the caveat array MUST be treated as a logically disjunct (an "OR", NOT an "and"). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: + +```json +{ + "dns:example.com?TYPE=TXT": { + "crud/create": [{}] + }, + "https://example.com/blog": { + "crud/read": [{}], + "crud/update": [ + {"status": "draft"}, + {"status": "published", "day-of-week": "Monday"} // only publish on Mondays + ] + } +} +``` + +The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. + +| Resource | Ability | Caveat | +|----------------------------|---------------|---------------------------------------------------| +| `dns:example.com?TYPE=TXT` | `crud/create` | Always | +| `https://example.com/blog` | `crud/read` | Always | +| `https://example.com/blog` | `crud/update` | `{status: "draft"}` | +| `https://example.com/blog` | `crud/update` | `{status: "published", "day-of-week": "monday"}` | + +The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. + +| Proof Caveats | Delegated Caveats | Is Valid? | Comment | +|---------------|-------------------|-----------|----------------------------------------| +| `[{}]` | `[{}]` | Yes | Equal | +| `[x]` | `[x]` | Yes | Equal | +| `[x]` | `[{}]` | No | Escalation to any | +| `[{}]` | `[x]` | Yes | Attenuates the `{}` caveat to `x` | +| `[x]` | `[y]` | No | Escalation by using a different caveat | +| `[x, y]` | `[x]` | Yes | Removes a capability | +| `[x, y]` | `[x, (y + z)]` | Yes | Attenuates existing caveat | +| `[x, y]` | `[x, y, z]` | No | Escalation by adding new capability | + +Note that for consistency in this syntax, the empty array MUST be equivalent to disallowing the capability. Conversely, an empty object MUST be treated as "no caveats". + +| Proof Caveats | Comment | +|---------------|---------------------------------------------------------------| +| `[]` | No capabilities | +| `[{}]` | Full capabilities for this resource/ability pair (no caveats) | + +### 3.2.7 Proof of Delegation + +Attenuations MUST be satisfied by matching the attenuated capability to a proof in the [`prf` array][prf field]. + +Proofs MUST be resolvable by the recipient. A proof MAY be left unresolvable if it is not used as support for the top-level UCAN's capability chain. The exact format MUST be defined in the relevant transport specification. Some examples of possible formats include: a JSON object payload delivered with the UCAN, a federated HTTP endpoint, a DHT, or a shared database. + +#### 3.2.7.1 `prf` Field + +The `prf` field MUST contain the [content address][content identifiers] of UCAN proofs (the "inputs" of a UCAN). + +#### 3.2.7.2 Examples + +``` json +"prf": [ + "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze", + "bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva" +] +``` + +Which in a JSON representation would resolve to the following table: + +```json +{ + "bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", + "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" +} +``` + +For more on this representation, please refer to [canonical collections]. + +# 4. Reserved Resources + +The following resources are REQUIRED to be implemented. + +## 4.1 `ucan` + +The `ucan` URI scheme defines URI selectors for UCANs. + +``` abnf +ucan = "ucan:" ["//" resource-owner-did "/"] ucan-selector +ucan-selector = "*" / uri-scheme / ucan-cid +``` + +| Syntax | Meaning | +|-------------------------|-----------------------------------------------| +| `ucan:` | A specific UCAN by [CID][content identifiers] | +| `ucan:*` | All possible provable UCANs | +| `ucan:./*` | All in this UCAN's proofs | +| `ucan:///*` | All of any scheme "owned" by a DID | +| `ucan:///` | All of scheme "owned" by a DID | + +`ucan:./*` represents all of the UCANs in the current proofs array. If selecting a particular proof (i.e. not the wildcard), then its CID MUST be used (`ucan:`). In the case of selecting a particular proof, the validator MUST check that the delegated content address is listed in the proofs (`prf`) field. + +`ucan:*` is very powerful and deserves special mention. It selects _any_ UCAN that the issuer has access to (including transitively), even if it is not in the proofs of the current UCAN. This is useful when delegating permissions to another agent, including all unknown future delegations to the issuer. + +### 4.1.1 `ucan:*` Example + +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`). + +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. + +``` mermaid +sequenceDiagram + autonumber + participant AliceRoot + participant AlicePhone + participant Bob + + AliceRoot ->> AlicePhone: ucan:* + Bob ->> AliceRoot: bobSharedDirectory + + Note over AliceRoot, Bob: Alice's Phone accesses Bob's Directory + Bob -->> AliceRoot: bobSharedDirectory + AliceRoot -->> AlicePhone: ucan:* + AlicePhone ->> Bob: Write into BobSharedDirectory +``` + +In the diagram above, solid lines are delegations. The dotted lines are proofs in a proof chain. `did:key:alicePhone` would includes both the proofs to connecting Bob to `did:key:aliceRoot`, and from `did:key:alicePhone` to `did:key:alicePhone`. Step 5 is `did:key:alicePhone` invoking that proof chain to access Bob's shared directory. + +# 5. Reserved Abilities + +The following abilities are REQUIRED to be implemented. + +## 5.1 UCAN Delegation + +The `ucan` scheme MUST accept the following ability: `ucan/*`. This ability redelegates all of the capabilities in the selected proof(s). Other resources MAY accept this ability as part of this semantics. In logical terms, the delegation ability is like stating "any ability for this resource". + +If an attenuated resource or capability is desired, it MUST be explicitly listed without the `ucan` URI scheme. + +``` js +{ + "ucan:bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": {"ucan/*": [{}]}, + "ucan:*": {"ucan/*": [{}]} +} +``` + +## 5.2 Top + +The "top" (or "super user") ability MUST be denoted `*`. The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. Top corresponds to an "all" matcher, whereas [delegation] corresponds to "any" in the UCAN chain. The top ability is useful when "linking" agents by delegating all access to resource(s). This is the most powerful ability, and as such it SHOULD be handled with care. + +``` mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' } } }%% + +flowchart BT + * + + msg/* --> * + subgraph msgGraph [ ] + msg/send --> msg/* + msg/receive --> msg/* + end + + crud/* --> * + subgraph crudGraph [ ] + crud/read --> crud/* + crud/mutate --> crud/* + + subgraph mutationGraph [ ] + crud/create --> crud/mutate + crud/update --> crud/mutate + crud/destroy --> crud/mutate + end + end + + ... --> * +``` + + +### 5.2.1 Bottom + +In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. + +# 6. Validation + +Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator SHOULD NOT reject UCANs with resources that it does not know how to interpret. + +If any of the following criteria are not met, the UCAN MUST be considered invalid. + +## 6.1 Time + +A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." + +All proofs MUST contain time bounds equal to or broader than the UCAN being delegated. If the proof expires before the outer UCAN — or starts after it — the reader MUST treat the UCAN as invalid. Delegation inside of the time bound is called "timely delegation." These conditions MUST hold even if the current wall clock time is inside of incorrectly delegated bounds. + +A UCAN is valid inclusive from the `nbf` time and until the `exp` field. If the current time is outside of these bounds, the UCAN MUST be considered invalid. When setting these bounds, a delegator or invoker SHOULD account for expected clock drift. Use of time bounds this way is called "timely invocation." + +## 6.2 Principal Alignment + +In delegation, the `aud` field of every proof MUST match the `iss` field of the outer UCAN (the one being delegated to). This alignment MUST form a chain back to the originating principal for each resource. + +This calculation MUST NOT take into account [DID fragment]s. If present, fragments are only intended to clarify which of a DID's keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired. + +``` mermaid +flowchart RL + owner[/Alice\] -. owns .-> resource[(Storage)] + executor[/"Compute Service"\] --> del2Aud + rootIss --> owner + + executor -. accesses .-> resource + rootCap -. references .-> resource + + subgraph root [Root UCAN] + rootIss(iss: Alice) + rootAud(aud: Bob) + rootCap("cap: (Storage, crud/*)") + end + + subgraph del1 [Delegated UCAN] + del1Iss(iss: Bob) --> rootAud + del1Aud(aud: Carol) + del1Cap("cap: (Storage, crud/*)") --> rootCap + end + + subgraph del2 [Final UCAN] + del2Iss(iss: Carol) --> del1Aud + del2Aud(aud: Compute Service) + del2Cap("cap: (Storage, crud/*)") --> del1Cap + end +``` + +In the above diagram, Alice has some storage. This storage may exist in one location with a single source of truth, but to help build intuition this example is location independent: local versions and remote stored copies are eventually consistent, and there is no one "correct" copy. As such, we list the owner (Alice) directly on the resource. + +Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the discharging compute service. The discharging service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. + +### 6.2.1 Recipient Validation + +An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. + +The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV`. Any other agent MUST NOT accept this UCAN. For example, `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` MUST NOT run the ability associated with that capability. + +``` js +{ + "aud": "did:key:zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", + "iss": "did:key:zAKJP3f7BD6W4iWEQ9jwndVTCBq8ua2Utt8EEjJ6Vxsf", + // ... +} +``` + +A good litmus test for invocation validity by a discharging agent is to check if they would be able to create a valid delegation for that capability. + +### 6.2.2 Token Uniqueness + +Each remote invocation MUST be a unique UCAN: for instance using a nonce (`nnc`) or simply a unique expiry. The recipient MUST validate that they have not received the top-level UCAN before. For implementation recommentations, please refer to the [replay attack prevention] section. + +## 6.3 Proof Chaining + +Each capability MUST either be originated by the issuer (root capability, or "parenthood") or have one-or-more proofs in the `prf` field to attest that this issuer is authorized to use that capability ("introduction"). In the introduction case, this check MUST be recursively applied to its proofs until a root proof is found (i.e. issued by the resource owner). + +Except for rights amplification (below), each capability delegation MUST have equal or narrower capabilities from its proofs. The time bounds MUST also be equal to or contained inside the time bounds of the proof's time bounds. This lowering of rights at each delegation is called "attenuation." + +## 6.4 Rights Amplification + +Some capabilities are more than the sum of their parts. The canonical example is a can of soup and a can opener. You need both to access the soup inside the can, but the can opener may come from a completely separate source than the can of soup. Such semantics MAY be implemented in UCAN capabilities. This means that validating particular capabilities MAY require more than one direct proof. The relevant proofs MAY be of a different resource and ability from the amplified capability. The delegated capability MUST have this behavior in its semantics, even if the proofs do not. + +## 6.5 Content Identifiers + +A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. + +The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other codecs are used (such as [`0x0129` `dag-json` multicodec][dag-json multicodec]), the UCAN MUST be able to be interpreted as a valid JWT (including the signature). + +The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. Please refer to [token resolution] for more. + +## 6.5.1 CID Canonicalization + +A canonical CID can be important for some use cases, such as caching and [revocation]. A canonical CID MUST conform to the following: + +* [CIDv1] +* [base32] +* [SHA2-256] +* [Raw data multicodec] (`0x55`) + +## 6.6 Revocation + +Any issuer of a UCAN MAY later revoke that UCAN or the capabilities that have been derived from it further downstream in a proof chain. + +This mechanism is eventually consistent and SHOULD be considered the last line of defense against abuse. Proactive expiry via time bounds or other constraints SHOULD be preferred, as they do not require learning more information than what would be available on an offline computer. + +While some resources are centralized (e.g. access to a server), others are unbound from specific locations (e.g. a CRDT), in which case it will take longer for the revocation to propagate. + +Every resource type SHOULD have a canonical location where its revocations are kept. This list is non-exclusive, and revocation messages MAY be gossiped between peers in a network to share this information more quickly. + +It is RECOMMENDED that the canonical revocation store be kept as close to (or inside) the resource it is about as possible. For example, the WebNative File System maintains a Merkle tree of revoked CIDs at a well-known path. Another example is that a centralized server could have an endpoint that lists the revoked UCANs by [canonical CID]. + +Revocations MUST be irreversible. If the revocation was issued in error, a unique UCAN MAY be issued (e.g. by updating the nonce or changing the time bounds). This prevents confusion as the revocation moves through the network and makes revocation stores append-only and highly amenable to caching. + +A revocation message MUST conform to the following JSON format: + +``` js +{ + "iss": did, + "revoke": canonicalUcanCid, + "challenge": base64Unpadded(sign(did.privateKey, `REVOKE:${canonicalUcanCid}`)) +} +``` + +This format makes it easy to select the relevant UCAN, confirm that the issuer is in the proof chain for some or all capabilities, and validate that the revocation was signed by the same issuer. + +Any other proofs in the selected UCAN not issued by the same DID as the revocation issuer MUST be treated as valid. + +Revocations MAY be deleted once the UCAN that they reference expires or otherwise becomes invalid via its proactive mechanisms. + +### 6.6.1 Example + +``` + Root + ┌────────────────┐ ─┐ + │ │ │ + │ iss: Alice │ │ + │ aud: Bob │ ├─ Alice can revoke + │ │ │ + └───┬────────┬───┘ │ + │ │ │ + │ │ │ + ▼ ▼ │ +┌──────────────┐ ┌──────────────┐ │ ─┐ +│ │ │ │ │ │ +│ iss: Bob │ │ iss: Bob │ │ │ +│ aud: Carol │ │ aud: Dan │ │ ├─ Bob can revoke +│ cap: [X,Y] │ │ cap: [Y,Z] │ │ │ +│ │ │ │ │ │ +└───────┬──────┘ └──┬───────────┘ │ │ + │ │ │ │ + │ │ │ │ + ▼ │ │ │ +┌──────────────┐ │ │ │ ─┐ +│ │ │ │ │ │ +│ iss: Carol │ │ │ │ │ +│ aud: Dan │ │ │ │ ├─ Carol can revoke +│ cap: [X,Y] │ │ │ │ │ +│ │ │ │ │ │ +└───────────┬──┘ │ │ │ │ + │ │ │ │ │ + │ │ │ │ │ + ▼ ▼ │ │ │ + ┌────────────────┐ │ │ │ ─┐ + │ │ │ │ │ │ + │ iss: Dan │ │ │ │ │ + │ aud: Erin │ │ │ │ ├─ Dan can revoke + │ cap: [X,Y,Z] │ │ │ │ │ + │ │ │ │ │ │ + └────────────────┘ ─┘ ─┘ ─┘ ─┘ +``` + +In this example, Alice MAY revoke any of the UCANs in the chain, Carol MAY revoke the bottom two, and so on. If the UCAN `Carol → Dan` is revoked by Alice, Bob, or Carol, then Erin will not have a valid chain for `X` since its proof is invalid. However, Erin can still prove the valid capability for `Y` and `Z` since the still-valid ("unbroken") chain `Alice → Bob → Dan → Erin` includes them. Note that despite `Y` being in the revoked `Carol → Dan` UCAN, it does not invalidate `Y` for Erin, since the unbroken chain also included a proof for `Y`. + +## 6.7 Backwards Compatibility + +A UCAN validator MAY implement backward compatibility with previous versions of UCAN. Delegated UCANs MUST be of an equal or higher version than their proofs. For example, a v0.9.0 UCAN that includes proofs that are separately v0.9.0, v0.8.1, v0.7.0, and v0.5.0 MAY be considered valid. A v0.5.0 UCAN that has a UCAN v0.9.0 proof MUST NOT be considered valid. + +# 7. Collections + +UCANs are indexed by their hash — often called their ["content address"][content addressable storage]. UCANs MUST be addressable as [CIDv1]. Use of a [canonical CID] is RECOMMENDED. + +Content addressing the proofs has multiple advantages over inlining tokens, including: +* Avoids re-encoding deeply nested proofs as Base64 many times (and the associated size increase) +* Canonical signature +* Enables only transmitting the relevant proofs + +Multiple UCANs in a single request MAY be collected into one table. It is RECOMMENDED that these be indexed by CID. The [canonical JSON representation][canonical collections] (below) MUST be supported. Implementations MAY include more formats, for example to optimize for a particular transport. Transports MAY map their collection to this collection format. + +### 7.1 Canonical JSON Collection + +The canonical JSON representation is an key-value object, mapping UCAN content identifiers to their fully-encoded base64url strings. A root "entry point" (if one exists) MUST be indexed by the slash `/` character. + +#### 7.1.1 Example + +``` json +{ + "/": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", + "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" +} +``` + +# 8. Token Resolution + +Token resolution is transport specific. The exact format is left to the relevant UCAN transport specification. At minimum, such a specification MUST define at least the following: + +1. Request protocol +2. Response protocol +3. Collections format + +Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics] of UCAN. + +# 9. Implementation Recommendations + +## 9.1 UCAN Store + +A validator MAY keep a local store of UCANs that it has received. UCANs are immutable but also time-bound so that this store MAY evict expired or revoked UCANs. + +This store MAY be indexed by CID (content addressing). Multiple indices built on top of this store MAY be used to improve capability search or selection performance. + +## 9.2 Memoized Validation + +Aside from revocation, capability validation is idempotent. Marking a CID (or capability index inside that CID) as valid acts as memoization, obviating the need to check the entire structure on every validation. This extends to distinct UCANs that share a proof: if the proof was previously reviewed and is not revoked, it is RECOMMENDED to consider it valid immediately. + +Revocation is irreversible. Suppose the validator learns of revocation by UCAN CID or issuer DID. In that case, the UCAN and all of its derivatives in such a cache MUST be marked as invalid, and all validations immediately fail without needing to walk the entire structure. + +## 9.3 Replay Attack Prevention + +Replay attack prevention is REQUIRED ([Token Uniqueness]). The exact strategy is left to the implementer. One simple strategy is maintaining a set of previously seen CIDs. This MAY be the same structure as a validated UCAN memoization table (if one exists in the implementation). + +It is RECOMMENDED that the structure have a secondary index referencing the token expiry field. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on. + +## 9.4 Session Content ID + +If many invocations are discharged during a session, the sender and receiver MAY agree to use the triple of CID, nonce, and signature rather than reissuing the complete UCAN chain for every message. This saves bandwidth and avoids needing to use another session token exchange mechanism or bearer token with lower security, such as a shared secret. + +```js +{ + "cid": cid(ucan) + "nnc": "ABC", + "sig": sign(ucan.iss.privateKey, cid(ucan) + "ABC") +} +``` + +# 10. Related Work and Prior Art + +[SPKI/SDSI] is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written. + +[ZCAP-LD] is closely related to UCAN. The primary differences are in formatting, addressing by URL instead of CID, the mechanism of separating invocation from authorization, and single versus multiple proofs. + +[CACAO] is a translation of many of these ideas to a cross-blockchain invocation model. It contains the same basic concepts but is aimed at small messages and identities that are rooted in mutable documents rooted on a blockchain and lacks the ability to subdelegate capabilities. + +[Local-First Auth] uses CRDT-based ACLs and key lockboxes for role-based signatures. This is a non-certificate-based approach, instead of relying on the CRDT and signed data to build up a list of roles and members. It does have a very friendly invitation certificate mechanism in [Seitan token exchange]. It is also straightforward to see which users have access to what, avoiding the confinement problem seen in many decentralized auth systems. + +[Macaroon] is a MAC-based capability and cookie system aimed at distributing authority across services in a trusted network (typically in the context of a Cloud). By not relying on asymmetric signatures, Macaroons achieve excellent space savings and performance, given that the MAC can be checked against the relevant services during discharge. The authority is rooted in an originating server rather than with an end-user. + +[Biscuit] uses Datalog to describe capabilities. It has a specialized format but is otherwise in line with UCAN. + +[Verifiable credentials] are a solution for data about people or organizations. However, they are aimed at a slightly different problem: asserting attributes about the holder of a DID, including things like work history, age, and membership. + +# 11. Acknowledgments + +Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. + +Many thanks to [Hugo Dias], [Mikael Rogers], and the entire DAG House team for the real world feedback, and finding inventive new use cases. + +Thank you [Blaine Cook] for the real-world feedback, ideas on future features, and lessons from other auth standards. + +Many thanks to [Brian Ginsburg] and [Steven Vandevelde] for their many copy edits, feedback from real world usage, maintenance of the TypeScript implementation, and tools such as [ucan.xyz]. + +Many thanks to [Christopher Joel] for his real-world feedback, raising many pragmatic considerations, and the Rust implementation and related crates. + +Many thanks to [Christine Lemmer-Webber] for her handwritten(!) feedback on the design of UCAN, spearheading the [OCapN] initiative, and her related work on [ZCAP-LD]. + +Thanks to [Benjamin Goering] for the many community threads and connections to [W3C] standards. + +Thanks to [Juan Caballero] for the numerous questions, clarifications, and general advice on putting together a comprehensible spec. + +Thank you [Dan Finlay] for being sufficiently passionate about [OCAP] that we realized that capability systems had a real chance of adoption in an ACL-dominated world. + +Thanks to the entire [SPKI WG][SPKI/SDSI] for their closely related pioneering work. + +Many thanks to [Alan Karp] for sharing his vast experience with capability-based authorization, patterns, and many right words for us to search for. + +We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. + +# 12. FAQ + +## 12.1 What prevents an unauthorized party from using an intercepted UCAN? + +UCANs always contain information about the sender and receiver. A UCAN is signed by the sender (the `iss` field DID) and can only be created by an agent in possession of the relevant private key. The recipient (the `aud` field DID) is required to check that the field matches their DID. These two checks together secure the certificate against use by an unauthorized party. + +## 12.2 What prevents replay attacks on the invocation use case? + +A UCAN delegated for purposes of immediate invocation MUST be unique. If many requests are to be made in quick succession, a nonce can be used. The receiving agent (the one to perform the invocation) checks the hash of the UCAN against a local store of unexpired UCAN hashes. + +This is not a concern when simply delegating since presumably the recipient agent already has that UCAN. + +## 12.3 Is UCAN secure against person-in-the-middle attacks? + +_UCAN does not have any special protection against person-in-the-middle (PITM) attacks._ + +Were a PITM attack successfully performed on a UCAN delegation, the proof chain would contain the attacker's DID(s). It is possible to detect this scenario and revoke the relevant UCAN but does require special inspection of the topmost `iss` field to check if it is the expected DID. Therefore, it is strongly RECOMMENDED to only delegate UCANs to agents that are both trusted and authenticated and over secure channels. + +[Alan Karp]: https://github.com/alanhkarp +[Benjamin Goering]: https://github.com/gobengo +[Biscuit]: https://github.com/biscuit-auth/biscuit/ +[Blaine Cook]: https://github.com/blaine +[Bluesky]: https://blueskyweb.xyz/ +[Brendan O'Brien]: https://github.com/b5 +[Brian Ginsburg]: https://github.com/bgins +[Brooklyn Zelenka]: https://github.com/expede +[CACAO]: https://blog.ceramic.network/capability-based-data-security-on-ceramic/ +[CIDv1]: https://docs.ipfs.io/concepts/content-addressing/#identifier-formats +[Canonical CID]: #651-cid-canonicalization +[Capability Myths Demolished]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf +[Christine Lemmer-Webber]: https://github.com/cwebber +[Christopher Joel]: https://github.com/cdata +[DID fragment]: https://www.w3.org/TR/did-core/#fragment +[DID path]: https://www.w3.org/TR/did-core/#path +[DID subject]: https://www.w3.org/TR/did-core/#dfn-did-subjects +[DID]: https://www.w3.org/TR/did-core/ +[Dan Finlay]: https://github.com/danfinlay +[Daniel Holmgren]: https://github.com/dholms +[ECDSA security]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Security +[FIDO]: https://fidoalliance.org/fido-authentication/ +[Fission]: https://fission.codes +[Hugo Dias]: https://github.com/hugomrdias +[Irakli Gozalishvili]: https://github.com/Gozala +[JWT]: https://datatracker.ietf.org/doc/html/rfc7519 +[Juan Caballero]: https://github.com/bumblefudge +[Local-First Auth]: https://github.com/local-first-web/auth +[Macaroon]: https://storage.googleapis.com/pub-tools-public-publication-data/pdf/41892.pdf +[Mark Miller]: https://github.com/erights +[Mikael Rogers]: https://github.com/mikeal/ +[OCAP]: http://erights.org/elib/capability/index.html +[OCapN]: https://github.com/ocapn/ocapn +[POLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege +[Philipp Krüger]: https://github.com/matheus23 +[Protocol Labs]: https://protocol.ai/ +[RFC 2119]: https://datatracker.ietf.org/doc/html/rfc2119 +[RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339 +[RFC 8037]: https://datatracker.ietf.org/doc/html/rfc8037 +[SHA2-256]: https://en.wikipedia.org/wiki/SHA-2 +[SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ +[SPKI]: https://theworld.com/~cme/html/spki.html +[Seitan token exchange]: https://book.keybase.io/docs/teams/seitan +[Steven Vandevelde]: https://github.com/icidasset +[Token Uniqueness]: #622-token-uniqueness +[URI]: https://www.rfc-editor.org/rfc/rfc3986 +[Verifiable credentials]: https://www.w3.org/2017/vc/WG/ +[W3C]: https://www.w3.org/ +[ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ +[`did:3`]: https://github.com/ceramicnetwork/CIPs/blob/main/CIPs/cip-79.md +[`did:ion`]: https://github.com/decentralized-identity/ion +[`did:key`]: https://w3c-ccg.github.io/did-method-key/ +[base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L12 +[browser api crypto key]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey +[canonical collections]: #71-canonical-json-collection +[capabilities]: https://en.wikipedia.org/wiki/Object-capability_model +[caps as keys]: http://www.erights.org/elib/capability/duals/myths.html#caps-as-keys +[confinement]: http://www.erights.org/elib/capability/dist-confine.html +[constructive semantics]: https://en.wikipedia.org/wiki/Intuitionistic_logic +[content addressable storage]: https://en.wikipedia.org/wiki/Content-addressable_storage +[content addressing]: https://en.wikipedia.org/wiki/Content-addressable_storage +[content identifiers]: #65-content-identifiers +[dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L104 +[delegation]: #51-ucan-delegation +[disjunction]: https://en.wikipedia.org/wiki/Logical_disjunction +[invocation]: https://github.com/ucan-wg/invocation +[prf field]: #3271-prf-field +[raw data multicodec]: https://github.com/multiformats/multicodec/blob/a03169371c0a4aec0083febc996c38c3846a0914/table.csv?plain=1#L41 +[replay attack prevention]: #93-replay-attack-prevention +[revocation]: #66-revocation +[rights amplification]: #64-rights-amplification +[secure hardware enclave]: https://support.apple.com/en-ca/guide/security/sec59b0b31ff +[spki rfc]: https://www.rfc-editor.org/rfc/rfc2693.html +[time definition]: https://en.wikipedia.org/wiki/Temporal_database +[token resolution]: #8-token-resolution +[top ability]: #41-top +[ucan.xyz]: https://ucan.xyz diff --git a/Scope.md b/Scope.md new file mode 100644 index 0000000..6baaada --- /dev/null +++ b/Scope.md @@ -0,0 +1,5 @@ +# Scope + +This document specifies a chained authorization certificate format. It is applicable to capability-based authorization and cryptographic challenge-responses for networked, peer-to-peer, Cloud, offline, local-first, cross-application, and blockchain use cases where capabilities need to be shared between principals without transmitting private key material. + +Any changes of Scope are not retroactive. From 36113ac4b974c6e03be0bcc273eaadfac787a8c6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 16:34:01 -0700 Subject: [PATCH 002/134] Remove confusing alanogy --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bcd66fe..a265aaf 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Two related models that work exceptionally well in the above context are Simple ## 1.2 Intuition -By analogy, ACLs are like a bouncer at an exclusive event. This bouncer has a list attendees allowed in and which of those are VIPs that get extra access. The attendees show their government-issued ID and are accepted or rejected. In addition, they may get a lanyard to identify that they have previously been allowed in. If someone is disruptive, they can simply be crossed off the list and denied further entry. +By analogy, ACLs are like a bouncer at an exclusive event. This bouncer has a list attendees allowed in and which of those are VIPs that get extra access. The attendees show their government-issued ID and are accepted or rejected. If there are many such events at many venues, the organizers need to coordinate ahead of time, denials need to be synchronized, and attendees need to show their ID cards to many bouncers. The likelihood of the bouncer letting in the wrong person due to synchronization lag or confusion by someone sharing a name is nonzero. @@ -399,12 +399,12 @@ The payload MUST describe the authorization claims, who is involved, and its val | Field | Type | Description | Required | |-------|------------------------------|---------------------------------------------|----------| -| `ucv` | `String` | UCAN Semantic Version (`0.2.0`) | Yes | +| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | | `iss` | `String` | Issuer DID (sender) | Yes | | `aud` | `String` | Audience DID (receiver) | Yes | | `nbf` | `Integer` | Not Before UTC Unix Timestamp (valid from) | No | | `exp` | `Integer \| null` | Expiration UTC Unix Timestamp (valid until) | Yes | -| `nnc` | `String` | Nonce | No | +| `nnc` | `String` | Nonce | Yes | | `fct` | `{String: Any}` | Facts (asserted, signed data) | No | | `cap` | `{URI: {Ability: [Object]}}` | Capabilities | Yes | | `prf` | `[CID]` | Proof of delegation (hash-linked UCANs) | No | From bb73f10c3c20580b34e306d05e9c570a7ce589e3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 16:42:53 -0700 Subject: [PATCH 003/134] Bump version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a265aaf..41e0ea5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# User Controlled Authorization Network (UCAN) Specification v0.10.0 +# User Controlled Authorization Network (UCAN) Specification v1.0.0-rc.1 ## Editors From b10407fd840bb6abe19b5dd67ae98591dccdb5a3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 16:52:25 -0700 Subject: [PATCH 004/134] Point at revocation & invocation specs --- README.md | 160 +++++++++++------------------------------------------- 1 file changed, 32 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 41e0ea5..b3155c7 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ For example, given the following authorities against a WebNative filesystem, the ```js // "wnfs" abilities: -// fetch < append < overwrite < superuser +// fetch < append < overwrite < superuser < top AuthorityA = { "wnfs://alice.example.com/pictures/": { @@ -281,7 +281,6 @@ Note that delegation is a separate concept from [invocation]. Delegation is the Attenuation is the process of constraining the capabilities in a delegation chain. - ### 2.8.1 Examples ``` json @@ -344,11 +343,11 @@ Attenuation is the process of constraining the capabilities in a delegation chai Revocation is the act of invalidating a UCAN after the fact, outside of the limitations placed on it by the UCAN's fields (such as its expiry). -In the case of UCAN, this MUST be done by a proof's issuer DID. For more on the exact mechanism, see the revocation validation section. +In the case of UCAN, this MUST be done by a proof's issuer DID. For more on the exact mechanism, see the [UCAN Revocation] spec. ## 2.10 Invocation -UCANs are used to delegate capabilities between DID-holding agents, eventually terminating in an "invocation" of those capabilities. Invocation is when the capability is exercised to perform some task on a resource. Invocation has its [own specification][invocation]. +UCANs are used to delegate capabilities between DID-holding agents, eventually terminating in an "invocation" of those capabilities. Invocation is when the capability is exercised to perform some task on a resource. Invocation has its [own specification][UCAN Invocation]. ## 2.11 Time @@ -806,109 +805,7 @@ A canonical CID can be important for some use cases, such as caching and [revoca * [SHA2-256] * [Raw data multicodec] (`0x55`) -## 6.6 Revocation - -Any issuer of a UCAN MAY later revoke that UCAN or the capabilities that have been derived from it further downstream in a proof chain. - -This mechanism is eventually consistent and SHOULD be considered the last line of defense against abuse. Proactive expiry via time bounds or other constraints SHOULD be preferred, as they do not require learning more information than what would be available on an offline computer. - -While some resources are centralized (e.g. access to a server), others are unbound from specific locations (e.g. a CRDT), in which case it will take longer for the revocation to propagate. - -Every resource type SHOULD have a canonical location where its revocations are kept. This list is non-exclusive, and revocation messages MAY be gossiped between peers in a network to share this information more quickly. - -It is RECOMMENDED that the canonical revocation store be kept as close to (or inside) the resource it is about as possible. For example, the WebNative File System maintains a Merkle tree of revoked CIDs at a well-known path. Another example is that a centralized server could have an endpoint that lists the revoked UCANs by [canonical CID]. - -Revocations MUST be irreversible. If the revocation was issued in error, a unique UCAN MAY be issued (e.g. by updating the nonce or changing the time bounds). This prevents confusion as the revocation moves through the network and makes revocation stores append-only and highly amenable to caching. - -A revocation message MUST conform to the following JSON format: - -``` js -{ - "iss": did, - "revoke": canonicalUcanCid, - "challenge": base64Unpadded(sign(did.privateKey, `REVOKE:${canonicalUcanCid}`)) -} -``` - -This format makes it easy to select the relevant UCAN, confirm that the issuer is in the proof chain for some or all capabilities, and validate that the revocation was signed by the same issuer. - -Any other proofs in the selected UCAN not issued by the same DID as the revocation issuer MUST be treated as valid. - -Revocations MAY be deleted once the UCAN that they reference expires or otherwise becomes invalid via its proactive mechanisms. - -### 6.6.1 Example - -``` - Root - ┌────────────────┐ ─┐ - │ │ │ - │ iss: Alice │ │ - │ aud: Bob │ ├─ Alice can revoke - │ │ │ - └───┬────────┬───┘ │ - │ │ │ - │ │ │ - ▼ ▼ │ -┌──────────────┐ ┌──────────────┐ │ ─┐ -│ │ │ │ │ │ -│ iss: Bob │ │ iss: Bob │ │ │ -│ aud: Carol │ │ aud: Dan │ │ ├─ Bob can revoke -│ cap: [X,Y] │ │ cap: [Y,Z] │ │ │ -│ │ │ │ │ │ -└───────┬──────┘ └──┬───────────┘ │ │ - │ │ │ │ - │ │ │ │ - ▼ │ │ │ -┌──────────────┐ │ │ │ ─┐ -│ │ │ │ │ │ -│ iss: Carol │ │ │ │ │ -│ aud: Dan │ │ │ │ ├─ Carol can revoke -│ cap: [X,Y] │ │ │ │ │ -│ │ │ │ │ │ -└───────────┬──┘ │ │ │ │ - │ │ │ │ │ - │ │ │ │ │ - ▼ ▼ │ │ │ - ┌────────────────┐ │ │ │ ─┐ - │ │ │ │ │ │ - │ iss: Dan │ │ │ │ │ - │ aud: Erin │ │ │ │ ├─ Dan can revoke - │ cap: [X,Y,Z] │ │ │ │ │ - │ │ │ │ │ │ - └────────────────┘ ─┘ ─┘ ─┘ ─┘ -``` - -In this example, Alice MAY revoke any of the UCANs in the chain, Carol MAY revoke the bottom two, and so on. If the UCAN `Carol → Dan` is revoked by Alice, Bob, or Carol, then Erin will not have a valid chain for `X` since its proof is invalid. However, Erin can still prove the valid capability for `Y` and `Z` since the still-valid ("unbroken") chain `Alice → Bob → Dan → Erin` includes them. Note that despite `Y` being in the revoked `Carol → Dan` UCAN, it does not invalidate `Y` for Erin, since the unbroken chain also included a proof for `Y`. - -## 6.7 Backwards Compatibility - -A UCAN validator MAY implement backward compatibility with previous versions of UCAN. Delegated UCANs MUST be of an equal or higher version than their proofs. For example, a v0.9.0 UCAN that includes proofs that are separately v0.9.0, v0.8.1, v0.7.0, and v0.5.0 MAY be considered valid. A v0.5.0 UCAN that has a UCAN v0.9.0 proof MUST NOT be considered valid. - -# 7. Collections - -UCANs are indexed by their hash — often called their ["content address"][content addressable storage]. UCANs MUST be addressable as [CIDv1]. Use of a [canonical CID] is RECOMMENDED. - -Content addressing the proofs has multiple advantages over inlining tokens, including: -* Avoids re-encoding deeply nested proofs as Base64 many times (and the associated size increase) -* Canonical signature -* Enables only transmitting the relevant proofs - -Multiple UCANs in a single request MAY be collected into one table. It is RECOMMENDED that these be indexed by CID. The [canonical JSON representation][canonical collections] (below) MUST be supported. Implementations MAY include more formats, for example to optimize for a particular transport. Transports MAY map their collection to this collection format. - -### 7.1 Canonical JSON Collection - -The canonical JSON representation is an key-value object, mapping UCAN content identifiers to their fully-encoded base64url strings. A root "entry point" (if one exists) MUST be indexed by the slash `/` character. - -#### 7.1.1 Example - -``` json -{ - "/": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", - "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" -} -``` - -# 8. Token Resolution +# _. Token Resolution Token resolution is transport specific. The exact format is left to the relevant UCAN transport specification. At minimum, such a specification MUST define at least the following: @@ -918,27 +815,27 @@ Token resolution is transport specific. The exact format is left to the relevant Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics] of UCAN. -# 9. Implementation Recommendations +# _. Implementation Recommendations -## 9.1 UCAN Store +## _.1 UCAN Store A validator MAY keep a local store of UCANs that it has received. UCANs are immutable but also time-bound so that this store MAY evict expired or revoked UCANs. This store MAY be indexed by CID (content addressing). Multiple indices built on top of this store MAY be used to improve capability search or selection performance. -## 9.2 Memoized Validation +## _.2 Memoized Validation Aside from revocation, capability validation is idempotent. Marking a CID (or capability index inside that CID) as valid acts as memoization, obviating the need to check the entire structure on every validation. This extends to distinct UCANs that share a proof: if the proof was previously reviewed and is not revoked, it is RECOMMENDED to consider it valid immediately. Revocation is irreversible. Suppose the validator learns of revocation by UCAN CID or issuer DID. In that case, the UCAN and all of its derivatives in such a cache MUST be marked as invalid, and all validations immediately fail without needing to walk the entire structure. -## 9.3 Replay Attack Prevention +## _.3 Replay Attack Prevention Replay attack prevention is REQUIRED ([Token Uniqueness]). The exact strategy is left to the implementer. One simple strategy is maintaining a set of previously seen CIDs. This MAY be the same structure as a validated UCAN memoization table (if one exists in the implementation). It is RECOMMENDED that the structure have a secondary index referencing the token expiry field. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on. -## 9.4 Session Content ID +## _.4 Session Content ID If many invocations are discharged during a session, the sender and receiver MAY agree to use the triple of CID, nonce, and signature rather than reissuing the complete UCAN chain for every message. This saves bandwidth and avoids needing to use another session token exchange mechanism or bearer token with lower security, such as a shared secret. @@ -950,7 +847,7 @@ If many invocations are discharged during a session, the sender and receiver MAY } ``` -# 10. Related Work and Prior Art +# _. Prior Art [SPKI/SDSI] is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written. @@ -966,7 +863,7 @@ If many invocations are discharged during a session, the sender and receiver MAY [Verifiable credentials] are a solution for data about people or organizations. However, they are aimed at a slightly different problem: asserting attributes about the holder of a DID, including things like work history, age, and membership. -# 11. Acknowledgments +# _. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. @@ -992,24 +889,39 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. -# 12. FAQ +# _. FAQ -## 12.1 What prevents an unauthorized party from using an intercepted UCAN? +## _.1 What prevents an unauthorized party from using an intercepted UCAN? UCANs always contain information about the sender and receiver. A UCAN is signed by the sender (the `iss` field DID) and can only be created by an agent in possession of the relevant private key. The recipient (the `aud` field DID) is required to check that the field matches their DID. These two checks together secure the certificate against use by an unauthorized party. -## 12.2 What prevents replay attacks on the invocation use case? +## _.2 What prevents replay attacks on the invocation use case? A UCAN delegated for purposes of immediate invocation MUST be unique. If many requests are to be made in quick succession, a nonce can be used. The receiving agent (the one to perform the invocation) checks the hash of the UCAN against a local store of unexpired UCAN hashes. This is not a concern when simply delegating since presumably the recipient agent already has that UCAN. -## 12.3 Is UCAN secure against person-in-the-middle attacks? +## _.3 Is UCAN secure against person-in-the-middle attacks? _UCAN does not have any special protection against person-in-the-middle (PITM) attacks._ Were a PITM attack successfully performed on a UCAN delegation, the proof chain would contain the attacker's DID(s). It is possible to detect this scenario and revoke the relevant UCAN but does require special inspection of the topmost `iss` field to check if it is the expected DID. Therefore, it is strongly RECOMMENDED to only delegate UCANs to agents that are both trusted and authenticated and over secure channels. + + +[Token Uniqueness]: #622-token-uniqueness +[canonical collections]: #71-canonical-json-collection +[content identifiers]: #65-content-identifiers +[delegation]: #51-ucan-delegation +[prf field]: #3271-prf-field +[replay attack prevention]: #93-replay-attack-prevention +[revocation]: #66-revocation +[rights amplification]: #64-rights-amplification +[token resolution]: #8-token-resolution +[top ability]: #41-top + + + [Alan Karp]: https://github.com/alanhkarp [Benjamin Goering]: https://github.com/gobengo [Biscuit]: https://github.com/biscuit-auth/biscuit/ @@ -1054,7 +966,8 @@ Were a PITM attack successfully performed on a UCAN delegation, the proof chain [SPKI]: https://theworld.com/~cme/html/spki.html [Seitan token exchange]: https://book.keybase.io/docs/teams/seitan [Steven Vandevelde]: https://github.com/icidasset -[Token Uniqueness]: #622-token-uniqueness +[UCAN Invocation]: https://github.com/ucan-wg/invocation +[UCAN Revocation]: https://github.com/ucan-wg/revocation [URI]: https://www.rfc-editor.org/rfc/rfc3986 [Verifiable credentials]: https://www.w3.org/2017/vc/WG/ [W3C]: https://www.w3.org/ @@ -1064,26 +977,17 @@ Were a PITM attack successfully performed on a UCAN delegation, the proof chain [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L12 [browser api crypto key]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey -[canonical collections]: #71-canonical-json-collection [capabilities]: https://en.wikipedia.org/wiki/Object-capability_model [caps as keys]: http://www.erights.org/elib/capability/duals/myths.html#caps-as-keys [confinement]: http://www.erights.org/elib/capability/dist-confine.html [constructive semantics]: https://en.wikipedia.org/wiki/Intuitionistic_logic [content addressable storage]: https://en.wikipedia.org/wiki/Content-addressable_storage [content addressing]: https://en.wikipedia.org/wiki/Content-addressable_storage -[content identifiers]: #65-content-identifiers [dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L104 -[delegation]: #51-ucan-delegation [disjunction]: https://en.wikipedia.org/wiki/Logical_disjunction [invocation]: https://github.com/ucan-wg/invocation -[prf field]: #3271-prf-field [raw data multicodec]: https://github.com/multiformats/multicodec/blob/a03169371c0a4aec0083febc996c38c3846a0914/table.csv?plain=1#L41 -[replay attack prevention]: #93-replay-attack-prevention -[revocation]: #66-revocation -[rights amplification]: #64-rights-amplification [secure hardware enclave]: https://support.apple.com/en-ca/guide/security/sec59b0b31ff [spki rfc]: https://www.rfc-editor.org/rfc/rfc2693.html [time definition]: https://en.wikipedia.org/wiki/Temporal_database -[token resolution]: #8-token-resolution -[top ability]: #41-top [ucan.xyz]: https://ucan.xyz From bf100fb040dcca8c536e6a77f3c7e60577c0a711 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 17:11:33 -0700 Subject: [PATCH 005/134] Clarification about time, move reserved URI to own spec --- README.md | 155 ++++++++++++++++++++++-------------------------------- 1 file changed, 63 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index b3155c7..bf452b7 100644 --- a/README.md +++ b/README.md @@ -396,18 +396,17 @@ Note that the JWT `"alg": "none"` option MUST NOT be supported. The lack of sign The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Description | Required | -|-------|------------------------------|---------------------------------------------|----------| -| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | -| `iss` | `String` | Issuer DID (sender) | Yes | -| `aud` | `String` | Audience DID (receiver) | Yes | -| `nbf` | `Integer` | Not Before UTC Unix Timestamp (valid from) | No | -| `exp` | `Integer \| null` | Expiration UTC Unix Timestamp (valid until) | Yes | -| `nnc` | `String` | Nonce | Yes | -| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | -| `cap` | `{URI: {Ability: [Object]}}` | Capabilities | Yes | -| `prf` | `[CID]` | Proof of delegation (hash-linked UCANs) | No | - +| Field | Type | Description | Required | +|-------|------------------------------|--------------------------------------------------------|----------| +| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | +| `iss` | `String` | Issuer DID (sender) | Yes | +| `aud` | `String` | Audience DID (receiver) | Yes | +| `nbf` | `Integer` | Not Before UTC Unix Timestamp in seconds (valid from) | No | +| `exp` | `Integer \| null` | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | +| `nnc` | `String` | Nonce | Yes | +| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | +| `cap` | `{URI: {Ability: [Object]}}` | Capabilities | Yes | +| `prf` | `[CID]` | Proof of delegation (hash-linked UCANs) | No | ### 3.2.1 Version @@ -447,7 +446,7 @@ It is RECOMMENDED that the underlying key types RSA, ECDSA, and EdDSA be support ### 3.2.3 Time Bounds -`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. +`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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range $-(2^{53} – 1)$ and $2^{53} – 1$ MUST be rejected as invalid. The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. @@ -455,11 +454,14 @@ The `exp` field MUST be set. Following the [principle of least authority][POLA], Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. -#### Examples +#### 3.2.3.1 Example -```json -"nbf": 1529496683, -"exp": 1575606941, +```js +{ + "nbf": 1529496683, + "exp": 1575606941, + // ... +} ``` ### 3.2.4 Nonce @@ -471,7 +473,10 @@ This field SHOULD NOT be used to sign arbitrary data, such as signature challeng #### Examples ``` json -"nnc": "1701-D" +{ + // ... + "nnc": "1701-D" +} ``` ### 3.2.5 Facts @@ -604,73 +609,13 @@ Which in a JSON representation would resolve to the following table: For more on this representation, please refer to [canonical collections]. -# 4. Reserved Resources - -The following resources are REQUIRED to be implemented. +# 4. Reserved Capabilities ## 4.1 `ucan` -The `ucan` URI scheme defines URI selectors for UCANs. - -``` abnf -ucan = "ucan:" ["//" resource-owner-did "/"] ucan-selector -ucan-selector = "*" / uri-scheme / ucan-cid -``` - -| Syntax | Meaning | -|-------------------------|-----------------------------------------------| -| `ucan:` | A specific UCAN by [CID][content identifiers] | -| `ucan:*` | All possible provable UCANs | -| `ucan:./*` | All in this UCAN's proofs | -| `ucan:///*` | All of any scheme "owned" by a DID | -| `ucan:///` | All of scheme "owned" by a DID | - -`ucan:./*` represents all of the UCANs in the current proofs array. If selecting a particular proof (i.e. not the wildcard), then its CID MUST be used (`ucan:`). In the case of selecting a particular proof, the validator MUST check that the delegated content address is listed in the proofs (`prf`) field. - -`ucan:*` is very powerful and deserves special mention. It selects _any_ UCAN that the issuer has access to (including transitively), even if it is not in the proofs of the current UCAN. This is useful when delegating permissions to another agent, including all unknown future delegations to the issuer. - -### 4.1.1 `ucan:*` Example - -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`). - -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. - -``` mermaid -sequenceDiagram - autonumber - participant AliceRoot - participant AlicePhone - participant Bob - - AliceRoot ->> AlicePhone: ucan:* - Bob ->> AliceRoot: bobSharedDirectory - - Note over AliceRoot, Bob: Alice's Phone accesses Bob's Directory - Bob -->> AliceRoot: bobSharedDirectory - AliceRoot -->> AlicePhone: ucan:* - AlicePhone ->> Bob: Write into BobSharedDirectory -``` - -In the diagram above, solid lines are delegations. The dotted lines are proofs in a proof chain. `did:key:alicePhone` would includes both the proofs to connecting Bob to `did:key:aliceRoot`, and from `did:key:alicePhone` to `did:key:alicePhone`. Step 5 is `did:key:alicePhone` invoking that proof chain to access Bob's shared directory. - -# 5. Reserved Abilities - -The following abilities are REQUIRED to be implemented. - -## 5.1 UCAN Delegation - -The `ucan` scheme MUST accept the following ability: `ucan/*`. This ability redelegates all of the capabilities in the selected proof(s). Other resources MAY accept this ability as part of this semantics. In logical terms, the delegation ability is like stating "any ability for this resource". - -If an attenuated resource or capability is desired, it MUST be explicitly listed without the `ucan` URI scheme. - -``` js -{ - "ucan:bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": {"ucan/*": [{}]}, - "ucan:*": {"ucan/*": [{}]} -} -``` +The `ucan` resource and abilty namespace MUST be reserved. Implementation of the [`ucan-uri`] spec is RECOMMENDED. -## 5.2 Top +## 4.2 "Top" Ability The "top" (or "super user") ability MUST be denoted `*`. The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. Top corresponds to an "all" matcher, whereas [delegation] corresponds to "any" in the UCAN chain. The top ability is useful when "linking" agents by delegating all access to resource(s). This is the most powerful ability, and as such it SHOULD be handled with care. @@ -701,18 +646,17 @@ flowchart BT ... --> * ``` - -### 5.2.1 Bottom +### 4.2.1 Bottom In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. -# 6. Validation +# _. Validation Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator SHOULD NOT reject UCANs with resources that it does not know how to interpret. If any of the following criteria are not met, the UCAN MUST be considered invalid. -## 6.1 Time +## _.1 Time A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." @@ -720,7 +664,21 @@ All proofs MUST contain time bounds equal to or broader than the UCAN being dele A UCAN is valid inclusive from the `nbf` time and until the `exp` field. If the current time is outside of these bounds, the UCAN MUST be considered invalid. When setting these bounds, a delegator or invoker SHOULD account for expected clock drift. Use of time bounds this way is called "timely invocation." -## 6.2 Principal Alignment + + + + + + +FIXME pseudocode + + + + + + + +## _.2 Principal Alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the outer UCAN (the one being delegated to). This alignment MUST form a chain back to the originating principal for each resource. @@ -758,7 +716,19 @@ In the above diagram, Alice has some storage. This storage may exist in one loca Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the discharging compute service. The discharging service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. -### 6.2.1 Recipient Validation + + + + + + + +FIXME pseudocode + + + + +### _.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. @@ -774,21 +744,21 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a discharging agent is to check if they would be able to create a valid delegation for that capability. -### 6.2.2 Token Uniqueness +### _.2.2 Token Uniqueness Each remote invocation MUST be a unique UCAN: for instance using a nonce (`nnc`) or simply a unique expiry. The recipient MUST validate that they have not received the top-level UCAN before. For implementation recommentations, please refer to the [replay attack prevention] section. -## 6.3 Proof Chaining +## _.3 Proof Chaining Each capability MUST either be originated by the issuer (root capability, or "parenthood") or have one-or-more proofs in the `prf` field to attest that this issuer is authorized to use that capability ("introduction"). In the introduction case, this check MUST be recursively applied to its proofs until a root proof is found (i.e. issued by the resource owner). Except for rights amplification (below), each capability delegation MUST have equal or narrower capabilities from its proofs. The time bounds MUST also be equal to or contained inside the time bounds of the proof's time bounds. This lowering of rights at each delegation is called "attenuation." -## 6.4 Rights Amplification +## _.4 Rights Amplification Some capabilities are more than the sum of their parts. The canonical example is a can of soup and a can opener. You need both to access the soup inside the can, but the can opener may come from a completely separate source than the can of soup. Such semantics MAY be implemented in UCAN capabilities. This means that validating particular capabilities MAY require more than one direct proof. The relevant proofs MAY be of a different resource and ability from the amplified capability. The delegated capability MUST have this behavior in its semantics, even if the proofs do not. -## 6.5 Content Identifiers +## _.5 Content Identifiers A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. @@ -796,7 +766,7 @@ The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other cod The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. Please refer to [token resolution] for more. -## 6.5.1 CID Canonicalization +## _.5.1 CID Canonicalization A canonical CID can be important for some use cases, such as caching and [revocation]. A canonical CID MUST conform to the following: @@ -990,4 +960,5 @@ Were a PITM attack successfully performed on a UCAN delegation, the proof chain [secure hardware enclave]: https://support.apple.com/en-ca/guide/security/sec59b0b31ff [spki rfc]: https://www.rfc-editor.org/rfc/rfc2693.html [time definition]: https://en.wikipedia.org/wiki/Temporal_database +[ucan-uri]: https://github.com/ucan-wg/ucan-uri [ucan.xyz]: https://ucan.xyz From 4346a7a564549d075a617fe0709f31e70770fcb6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 20:36:18 -0700 Subject: [PATCH 006/134] Switch diagram to Mermaid; remove item that confused Quinn --- README.md | 71 ++++++++++++++++++++----------------------------------- 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index bf452b7..3f3453e 100644 --- a/README.md +++ b/README.md @@ -91,53 +91,34 @@ Unlike many authorization systems where a service controls access to resources i There are several roles that an agent MAY assume: -| Name | Description | -| --------- | ----------- | -| Agent | The general class of entities and principals that interact with a UCAN | +| Name | Description | +| --------- | ------------------------------------------------------------------------------------------------ | +| Agent | The general class of entities and principals that interact with a UCAN | | Validator | Any agent that interprets a UCAN to determine that it is valid, and which capabilities it grants | -| Principal | An agent identified by DID (listed in a UCAN's `iss` or `aud` field) | -| Audience | The principal delegated to in the current UCAN. Listed in the `aud` field | -| Signer | A principal that can sign payloads | -| Issuer | The signer of the current UCAN. Listed in the `iss` field | -| Revoker | The issuer listed in a proof chain that revokes a UCAN | -| Owner | The root issuer of a capability, who has some proof that they fully control the resource | +| Principal | An agent identified by DID (listed in a UCAN's `iss` or `aud` field) | +| Audience | The principal delegated to in the current UCAN. Listed in the `aud` field | +| Issuer | The signer of the current UCAN. Listed in the `iss` field | +| Revoker | The issuer listed in a proof chain that revokes a UCAN | +| Owner | The root issuer of a capability, who has some proof that they fully control the resource | -``` -┌────────────────────────────────────────────────────────────────────────────────────────────┐ -│ │ -│ Agent │ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ ┌──────────────────────┐ │ -│ │ │ │ │ │ -│ │ Principal │ │ Validator │ │ -│ │ │ │ │ │ -│ │ ┌──────────────────────────┐ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ Signer │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ ┌────────────────────┐ │ ┌──────────────────────┐ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ Issuer │ │ │ Audience │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ │ Owner │ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ │ Revoker │ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ │ │ │ │ -│ │ │ └────────────────────┘ │ └──────────────────────┘ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ └──────────────────────────┘ │ │ │ │ -│ │ │ │ │ │ -│ └──────────────────────────────────────────────────────────┘ └──────────────────────┘ │ -│ │ -└────────────────────────────────────────────────────────────────────────────────────────────┘ +``` mermaid +flowchart TD + subgraph Agent + subgraph Principal + direction TB + + subgraph Issuer + direction TB + + Owner + Revoker + end + + Audience + end + + Validator + end ``` ## 2.2 Resource From a43392451e9a47be671694d302232b6ae86a169b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 20:50:33 -0700 Subject: [PATCH 007/134] Recommended key types --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f3453e..dc1bcd5 100644 --- a/README.md +++ b/README.md @@ -395,13 +395,17 @@ The `ucv` field sets the version of the UCAN specification used in the payload. ### 3.2.2 Principals -The `iss` and `aud` fields describe the token's principals. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. +The `iss` and `aud` fields describe the token's principals. They are distinguished by having DIDs. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. The `iss` and `aud` fields MUST contain a single principal each. If an issuer's DID has more than one key (e.g. [`did:ion`], [`did:3`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. -It is RECOMMENDED that the underlying key types RSA, ECDSA, and EdDSA be supported. +It is RECOMMENDED that the following `did:key` types be supported: + +- [RSA][did:key RSA] +- [EdDSA][did:key EdDSA] +- [P-256 ECDSA][did:key ECDSA] #### Examples @@ -935,6 +939,9 @@ Were a PITM attack successfully performed on a UCAN delegation, the proof chain [content addressable storage]: https://en.wikipedia.org/wiki/Content-addressable_storage [content addressing]: https://en.wikipedia.org/wiki/Content-addressable_storage [dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L104 +[did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 +[did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 +[did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [disjunction]: https://en.wikipedia.org/wiki/Logical_disjunction [invocation]: https://github.com/ucan-wg/invocation [raw data multicodec]: https://github.com/multiformats/multicodec/blob/a03169371c0a4aec0083febc996c38c3846a0914/table.csv?plain=1#L41 From 275d82a07465f32d79bc63f0d0c553e736583dae Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 20:52:42 -0700 Subject: [PATCH 008/134] Clarify int bounds --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc1bcd5..399d17a 100644 --- a/README.md +++ b/README.md @@ -431,7 +431,7 @@ It is RECOMMENDED that the following `did:key` types be supported: ### 3.2.3 Time Bounds -`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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range $-(2^{53} – 1)$ and $2^{53} – 1$ MUST be rejected as invalid. +`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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. From adec789124b34280746fea135d600ea9d7d1ef6b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 21:01:52 -0700 Subject: [PATCH 009/134] Formatting --- README.md | 64 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 399d17a..6979d9d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. -# 1. Introduction +# 1 Introduction ## 1.1 Motivation @@ -85,7 +85,7 @@ Unlike many authorization systems where a service controls access to resources i └─────────────┘ └─────────────┘ └─────────────┘ ``` -# 2. Terminology +# 2 Terminology ## 2.1 Roles @@ -346,9 +346,9 @@ The moment at which a delegation was asserted. This MAY be captured via an `iat` Decision time is the part of the lifecycle when "a decision" about the token is made. This is typically during validation, but also includes resolving external state (e.g. storage quotas). -# 3. JWT Structure +# 3 JWT Structure -UCANs MUST be formatted as JWTs, with additional keys as described in this document. The overall container of a header, claims, and signature remains. Please refer to [RFC 7519][JWT] for more on this format. +UCANs MUST be formatted as [JWT]s, with additional keys as described in this document. The overall container of a header, claims, and signature remains. Please refer to [RFC 7519][JWT] for more on this format. ## 3.1 Header @@ -364,7 +364,7 @@ EdDSA, as applied to JOSE (including JWT), is described in [RFC 8037]. Note that the JWT `"alg": "none"` option MUST NOT be supported. The lack of signature prevents the issuer from being validatable. -### Examples +### 3.1.1 Examples ```json { @@ -407,7 +407,7 @@ It is RECOMMENDED that the following `did:key` types be supported: - [EdDSA][did:key EdDSA] - [P-256 ECDSA][did:key ECDSA] -#### Examples +#### 3.2.2.1 Examples ```json "aud": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", @@ -453,9 +453,11 @@ Keeping the window of validity as short as possible is RECOMMENDED. Limiting the The OPTIONAL nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. -This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the `fct` field for more. +The recommeneded size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's cryptosuite. -#### Examples +This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`fct`][Facts] field for more. + +#### 3.2.4.1 Examples ``` json { @@ -468,7 +470,7 @@ This field SHOULD NOT be used to sign arbitrary data, such as signature challeng The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. -#### Examples +#### 3.2.5.1 Examples ``` json { @@ -594,7 +596,7 @@ Which in a JSON representation would resolve to the following table: For more on this representation, please refer to [canonical collections]. -# 4. Reserved Capabilities +# 4 Reserved Namespaces ## 4.1 `ucan` @@ -635,13 +637,13 @@ flowchart BT In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. -# _. Validation +# 5 Validation Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator SHOULD NOT reject UCANs with resources that it does not know how to interpret. If any of the following criteria are not met, the UCAN MUST be considered invalid. -## _.1 Time +## 5.1 Time A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." @@ -663,7 +665,7 @@ FIXME pseudocode -## _.2 Principal Alignment +## 5.2 Principal Alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the outer UCAN (the one being delegated to). This alignment MUST form a chain back to the originating principal for each resource. @@ -713,7 +715,7 @@ FIXME pseudocode -### _.2.1 Recipient Validation +### 5.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. @@ -729,21 +731,21 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a discharging agent is to check if they would be able to create a valid delegation for that capability. -### _.2.2 Token Uniqueness +### 5.2.2 Token Uniqueness Each remote invocation MUST be a unique UCAN: for instance using a nonce (`nnc`) or simply a unique expiry. The recipient MUST validate that they have not received the top-level UCAN before. For implementation recommentations, please refer to the [replay attack prevention] section. -## _.3 Proof Chaining +## 5.3 Proof Chaining Each capability MUST either be originated by the issuer (root capability, or "parenthood") or have one-or-more proofs in the `prf` field to attest that this issuer is authorized to use that capability ("introduction"). In the introduction case, this check MUST be recursively applied to its proofs until a root proof is found (i.e. issued by the resource owner). Except for rights amplification (below), each capability delegation MUST have equal or narrower capabilities from its proofs. The time bounds MUST also be equal to or contained inside the time bounds of the proof's time bounds. This lowering of rights at each delegation is called "attenuation." -## _.4 Rights Amplification +## 5.4 Rights Amplification Some capabilities are more than the sum of their parts. The canonical example is a can of soup and a can opener. You need both to access the soup inside the can, but the can opener may come from a completely separate source than the can of soup. Such semantics MAY be implemented in UCAN capabilities. This means that validating particular capabilities MAY require more than one direct proof. The relevant proofs MAY be of a different resource and ability from the amplified capability. The delegated capability MUST have this behavior in its semantics, even if the proofs do not. -## _.5 Content Identifiers +## 5.5 Content Identifiers A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. @@ -751,7 +753,7 @@ The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other cod The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. Please refer to [token resolution] for more. -## _.5.1 CID Canonicalization +## 5.5.1 CID Canonicalization A canonical CID can be important for some use cases, such as caching and [revocation]. A canonical CID MUST conform to the following: @@ -760,7 +762,7 @@ A canonical CID can be important for some use cases, such as caching and [revoca * [SHA2-256] * [Raw data multicodec] (`0x55`) -# _. Token Resolution +# 6 Token Resolution Token resolution is transport specific. The exact format is left to the relevant UCAN transport specification. At minimum, such a specification MUST define at least the following: @@ -770,27 +772,27 @@ Token resolution is transport specific. The exact format is left to the relevant Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics] of UCAN. -# _. Implementation Recommendations +# 7 Implementation Recommendations -## _.1 UCAN Store +## 7.1 UCAN Store A validator MAY keep a local store of UCANs that it has received. UCANs are immutable but also time-bound so that this store MAY evict expired or revoked UCANs. This store MAY be indexed by CID (content addressing). Multiple indices built on top of this store MAY be used to improve capability search or selection performance. -## _.2 Memoized Validation +## 7.2 Memoized Validation Aside from revocation, capability validation is idempotent. Marking a CID (or capability index inside that CID) as valid acts as memoization, obviating the need to check the entire structure on every validation. This extends to distinct UCANs that share a proof: if the proof was previously reviewed and is not revoked, it is RECOMMENDED to consider it valid immediately. Revocation is irreversible. Suppose the validator learns of revocation by UCAN CID or issuer DID. In that case, the UCAN and all of its derivatives in such a cache MUST be marked as invalid, and all validations immediately fail without needing to walk the entire structure. -## _.3 Replay Attack Prevention +## 7.3 Replay Attack Prevention Replay attack prevention is REQUIRED ([Token Uniqueness]). The exact strategy is left to the implementer. One simple strategy is maintaining a set of previously seen CIDs. This MAY be the same structure as a validated UCAN memoization table (if one exists in the implementation). It is RECOMMENDED that the structure have a secondary index referencing the token expiry field. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on. -## _.4 Session Content ID +## 7.4 Session Content ID If many invocations are discharged during a session, the sender and receiver MAY agree to use the triple of CID, nonce, and signature rather than reissuing the complete UCAN chain for every message. This saves bandwidth and avoids needing to use another session token exchange mechanism or bearer token with lower security, such as a shared secret. @@ -802,7 +804,7 @@ If many invocations are discharged during a session, the sender and receiver MAY } ``` -# _. Prior Art +# 8 Prior Art [SPKI/SDSI] is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written. @@ -818,7 +820,7 @@ If many invocations are discharged during a session, the sender and receiver MAY [Verifiable credentials] are a solution for data about people or organizations. However, they are aimed at a slightly different problem: asserting attributes about the holder of a DID, including things like work history, age, and membership. -# _. Acknowledgments +# 9. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. @@ -844,19 +846,19 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. -# _. FAQ +# 10. FAQ -## _.1 What prevents an unauthorized party from using an intercepted UCAN? +## 10.1 What prevents an unauthorized party from using an intercepted UCAN? UCANs always contain information about the sender and receiver. A UCAN is signed by the sender (the `iss` field DID) and can only be created by an agent in possession of the relevant private key. The recipient (the `aud` field DID) is required to check that the field matches their DID. These two checks together secure the certificate against use by an unauthorized party. -## _.2 What prevents replay attacks on the invocation use case? +## 10.2 What prevents replay attacks on the invocation use case? A UCAN delegated for purposes of immediate invocation MUST be unique. If many requests are to be made in quick succession, a nonce can be used. The receiving agent (the one to perform the invocation) checks the hash of the UCAN against a local store of unexpired UCAN hashes. This is not a concern when simply delegating since presumably the recipient agent already has that UCAN. -## _.3 Is UCAN secure against person-in-the-middle attacks? +## 10.3 Is UCAN secure against person-in-the-middle attacks? _UCAN does not have any special protection against person-in-the-middle (PITM) attacks._ From b53c45079c6b0985d79c33e11ba5967a3dbc7312 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 21:08:41 -0700 Subject: [PATCH 010/134] no more "discharge" :laugh: --- README.md | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 6979d9d..8e4afd2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ # 0. Abstract -User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first, user-originated authorization and revocation scheme. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. UCAN allows for the creation and discharge of authority by any agent with a DID, including traditional systems and peer-to-peer architectures beyond traditional cloud computing. +User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first, user-originated authorization and revocation scheme. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. UCAN allows for the creation, delegation, and invocation of authority by any agent with a DID, including traditional systems and peer-to-peer architectures beyond traditional cloud computing. ## Language @@ -51,7 +51,7 @@ Each UCAN includes a constructive set of assertions of what it is allowed to do. This signature chain is the root of trust. Private keys themselves SHOULD NOT move from one context to another: this is what the delegation mechanism provides: "sharing authority without sharing keys." -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. +UCANs (and other forms of PKI) depend on the ambient authority of the owner of each resource. This means that the invoking agent must be able to verify the root ownership at decision time. The rest of the chain in-between is self-certifying. 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. @@ -701,7 +701,7 @@ flowchart RL In the above diagram, Alice has some storage. This storage may exist in one location with a single source of truth, but to help build intuition this example is location independent: local versions and remote stored copies are eventually consistent, and there is no one "correct" copy. As such, we list the owner (Alice) directly on the resource. -Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the discharging compute service. The discharging service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. +Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the invoking compute service. The invoking service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. @@ -729,7 +729,7 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM } ``` -A good litmus test for invocation validity by a discharging agent is to check if they would be able to create a valid delegation for that capability. +A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. ### 5.2.2 Token Uniqueness @@ -792,18 +792,6 @@ Replay attack prevention is REQUIRED ([Token Uniqueness]). The exact strategy is It is RECOMMENDED that the structure have a secondary index referencing the token expiry field. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on. -## 7.4 Session Content ID - -If many invocations are discharged during a session, the sender and receiver MAY agree to use the triple of CID, nonce, and signature rather than reissuing the complete UCAN chain for every message. This saves bandwidth and avoids needing to use another session token exchange mechanism or bearer token with lower security, such as a shared secret. - -```js -{ - "cid": cid(ucan) - "nnc": "ABC", - "sig": sign(ucan.iss.privateKey, cid(ucan) + "ABC") -} -``` - # 8 Prior Art [SPKI/SDSI] is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written. @@ -814,7 +802,7 @@ If many invocations are discharged during a session, the sender and receiver MAY [Local-First Auth] uses CRDT-based ACLs and key lockboxes for role-based signatures. This is a non-certificate-based approach, instead of relying on the CRDT and signed data to build up a list of roles and members. It does have a very friendly invitation certificate mechanism in [Seitan token exchange]. It is also straightforward to see which users have access to what, avoiding the confinement problem seen in many decentralized auth systems. -[Macaroon] is a MAC-based capability and cookie system aimed at distributing authority across services in a trusted network (typically in the context of a Cloud). By not relying on asymmetric signatures, Macaroons achieve excellent space savings and performance, given that the MAC can be checked against the relevant services during discharge. The authority is rooted in an originating server rather than with an end-user. +[Macaroon] is a MAC-based capability and cookie system aimed at distributing authority across services in a trusted network (typically in the context of a Cloud). By not relying on asymmetric signatures, Macaroons achieve excellent space savings and performance, given that the MAC can be checked against the relevant services during invocation. The authority is rooted in an originating server rather than with an end-user. [Biscuit] uses Datalog to describe capabilities. It has a specialized format but is otherwise in line with UCAN. From 87b262ea1472b11884d4e1c8f02af54021b85c31 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 21:24:37 -0700 Subject: [PATCH 011/134] Footnote on IEEE754 --- README.md | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8e4afd2..14df270 100644 --- a/README.md +++ b/README.md @@ -431,7 +431,7 @@ It is RECOMMENDED that the following `did:key` types be supported: ### 3.2.3 Time Bounds -`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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. +`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 MUST represent seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. @@ -439,6 +439,8 @@ The `exp` field MUST be set. Following the [principle of least authority][POLA], Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. +[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined them as [IEEE-754] double-precision floating point number, which is 52-bits. + #### 3.2.3.1 Example ```js @@ -834,24 +836,6 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. -# 10. FAQ - -## 10.1 What prevents an unauthorized party from using an intercepted UCAN? - -UCANs always contain information about the sender and receiver. A UCAN is signed by the sender (the `iss` field DID) and can only be created by an agent in possession of the relevant private key. The recipient (the `aud` field DID) is required to check that the field matches their DID. These two checks together secure the certificate against use by an unauthorized party. - -## 10.2 What prevents replay attacks on the invocation use case? - -A UCAN delegated for purposes of immediate invocation MUST be unique. If many requests are to be made in quick succession, a nonce can be used. The receiving agent (the one to perform the invocation) checks the hash of the UCAN against a local store of unexpired UCAN hashes. - -This is not a concern when simply delegating since presumably the recipient agent already has that UCAN. - -## 10.3 Is UCAN secure against person-in-the-middle attacks? - -_UCAN does not have any special protection against person-in-the-middle (PITM) attacks._ - -Were a PITM attack successfully performed on a UCAN delegation, the proof chain would contain the attacker's DID(s). It is possible to detect this scenario and revoke the relevant UCAN but does require special inspection of the topmost `iss` field to check if it is the expected DID. Therefore, it is strongly RECOMMENDED to only delegate UCANs to agents that are both trusted and authenticated and over secure channels. - [Token Uniqueness]: #622-token-uniqueness @@ -891,7 +875,9 @@ Were a PITM attack successfully performed on a UCAN delegation, the proof chain [FIDO]: https://fidoalliance.org/fido-authentication/ [Fission]: https://fission.codes [Hugo Dias]: https://github.com/hugomrdias +[IEEE 754]: https://ieeexplore.ieee.org/document/8766229 [Irakli Gozalishvili]: https://github.com/Gozala +[JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number [JWT]: https://datatracker.ietf.org/doc/html/rfc7519 [Juan Caballero]: https://github.com/bumblefudge [Local-First Auth]: https://github.com/local-first-web/auth From e37bfcbe78526ce1b204c20113d9b088999540f8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 21:46:04 -0700 Subject: [PATCH 012/134] Time valuidation pseudocode --- README.md | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 14df270..7bdafac 100644 --- a/README.md +++ b/README.md @@ -439,7 +439,7 @@ The `exp` field MUST be set. Following the [principle of least authority][POLA], Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. -[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined them as [IEEE-754] double-precision floating point number, which is 52-bits. +[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. #### 3.2.3.1 Example @@ -538,7 +538,7 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" } } ``` - + The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. | Resource | Ability | Caveat | @@ -649,23 +649,39 @@ If any of the following criteria are not met, the UCAN MUST be considered invali A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." -All proofs MUST contain time bounds equal to or broader than the UCAN being delegated. If the proof expires before the outer UCAN — or starts after it — the reader MUST treat the UCAN as invalid. Delegation inside of the time bound is called "timely delegation." These conditions MUST hold even if the current wall clock time is inside of incorrectly delegated bounds. +All proofs MUST contain time bounds equal to or broader than the UCAN being delegated (i.e. delegation maintains or narrows the time bounds). If the proof expires before the delegated UCAN — or starts after it — the validator MUST treat the entire UCAN as invalid. Delegation inside of the time bound is called "timely delegation." These conditions MUST hold even if the current wall clock time is inside of incorrectly delegated bounds. A UCAN is valid inclusive from the `nbf` time and until the `exp` field. If the current time is outside of these bounds, the UCAN MUST be considered invalid. When setting these bounds, a delegator or invoker SHOULD account for expected clock drift. Use of time bounds this way is called "timely invocation." +``` js +// Pseudocode +const ensureTimeBounds = (ucan) => { + if (!!proof.nbf && ucan.exp !== null && ucan.nbf >= ucan.exp) { + throw new Error("Time violation: UCAN starts after expiry") + } +} +const ensureProofNbf = (ucan, proof) => { + if (!!proof.nbf && ucan.nbf <= proof.nbf) { + throw new Error("Time escelation: delegation starts before proof starts") + } +} +const ensureProofExp = (ucan, proof) => { + if (proof.exp !== null && ucan.exp > proof.exp) { + throw new Error("Time escelation: delegation ends after proof ends") + } +} - - - -FIXME pseudocode - - - - - - +const ensureTime = async (ucan) => { + checkTimeBounds(ucan) + ucan.proofs.forEach((cid) => { + const proof = await getUcan(cid) + ensureProofNbf(ucan, proof) + ensureProofExp(ucan, proof) + }) +} +``` ## 5.2 Principal Alignment From a475d975bf2708c59e6d2a9cb0a6e8e0eeaca34f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 21:49:21 -0700 Subject: [PATCH 013/134] Fix typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7bdafac..22fac2b 100644 --- a/README.md +++ b/README.md @@ -674,12 +674,12 @@ const ensureProofExp = (ucan, proof) => { } const ensureTime = async (ucan) => { - checkTimeBounds(ucan) - ucan.proofs.forEach((cid) => { + ensureTimeBounds(ucan) + await Promise.all(ucan.proofs.forEach(async (cid) => { const proof = await getUcan(cid) ensureProofNbf(ucan, proof) ensureProofExp(ucan, proof) - }) + })) } ``` From a59b211fcf6967469eb447934f3fdb419c3c4b9c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 22:07:45 -0700 Subject: [PATCH 014/134] Fix logic in pseudocode --- README.md | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 22fac2b..80df37f 100644 --- a/README.md +++ b/README.md @@ -655,14 +655,26 @@ A UCAN is valid inclusive from the `nbf` time and until the `exp` field. If the ``` js // Pseudocode + +const ensureTime = async (ucan) => { + ensureTimeBounds(ucan) + await Promise.all(ucan.prf.forEach(async (cid) => { + const proof = await getUcan(cid) + ensureProofNbf(ucan, proof) + ensureProofExp(ucan, proof) + })) +} + +// Helpers + const ensureTimeBounds = (ucan) => { - if (!!proof.nbf && ucan.exp !== null && ucan.nbf >= ucan.exp) { + if (!!proof.nbf && ucan.exp !== null && ucan.nbf > ucan.exp) { throw new Error("Time violation: UCAN starts after expiry") } } const ensureProofNbf = (ucan, proof) => { - if (!!proof.nbf && ucan.nbf <= proof.nbf) { + if (!!proof.nbf && ucan.nbf < proof.nbf) { throw new Error("Time escelation: delegation starts before proof starts") } } @@ -672,15 +684,6 @@ const ensureProofExp = (ucan, proof) => { throw new Error("Time escelation: delegation ends after proof ends") } } - -const ensureTime = async (ucan) => { - ensureTimeBounds(ucan) - await Promise.all(ucan.proofs.forEach(async (cid) => { - const proof = await getUcan(cid) - ensureProofNbf(ucan, proof) - ensureProofExp(ucan, proof) - })) -} ``` ## 5.2 Principal Alignment @@ -721,18 +724,6 @@ In the above diagram, Alice has some storage. This storage may exist in one loca Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the invoking compute service. The invoking service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. - - - - - - - -FIXME pseudocode - - - - ### 5.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. From b1374b3a5fa67b192aefca3b8e05bf71a2fa6559 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 22:10:00 -0700 Subject: [PATCH 015/134] Rmeove (all but one) use of inner/outer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80df37f..d7c18eb 100644 --- a/README.md +++ b/README.md @@ -688,7 +688,7 @@ const ensureProofExp = (ucan, proof) => { ## 5.2 Principal Alignment -In delegation, the `aud` field of every proof MUST match the `iss` field of the outer UCAN (the one being delegated to). This alignment MUST form a chain back to the originating principal for each resource. +In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. This calculation MUST NOT take into account [DID fragment]s. If present, fragments are only intended to clarify which of a DID's keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired. From 3ba4d26da68ab65f934a5611dccb8905ae563e3a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Aug 2023 22:14:30 -0700 Subject: [PATCH 016/134] Clarify that not all capabilties are immedietly invalidated --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d7c18eb..8681d9d 100644 --- a/README.md +++ b/README.md @@ -641,11 +641,11 @@ In concept there is a "bottom" ability ("none" or "void"), but it is not possibl # 5 Validation -Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator SHOULD NOT reject UCANs with resources that it does not know how to interpret. +Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator MUST NOT reject all capabilities when only one is not understood. -If any of the following criteria are not met, the UCAN MUST be considered invalid. +If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: -## 5.1 Time +## 5.1 Time Bounds A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." From dce0876776db41b21ba7840e0ea3873f092b8139 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 6 Oct 2023 15:57:43 -0700 Subject: [PATCH 017/134] Start ripping out the stuff that's now in the high level spec --- README.md | 235 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 120 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 8681d9d..3207e20 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,21 @@ -# User Controlled Authorization Network (UCAN) Specification v1.0.0-rc.1 +# UCAN Delegation Specification v1.0.0-rc.1 + + + +TODO +- Remove general motivation section, narrow to delegation + + + + + + + + + + + + ## Editors @@ -11,14 +28,20 @@ * [Irakli Gozalishvili], [Protocol Labs] * [Philipp Krüger], [Fission] -# 0. Abstract +## Dependencies -User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first, user-originated authorization and revocation scheme. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. UCAN allows for the creation, delegation, and invocation of authority by any agent with a DID, including traditional systems and peer-to-peer architectures beyond traditional cloud computing. +* [UCAN] +* [DID] +* [JWT] ## Language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. +# 0 Abstract + +UCAN Delegation is a component of the [UCAN] specification. This specification describes the semantics and serialization format for [UCAN] delegation between principals. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. + # 1 Introduction ## 1.1 Motivation @@ -55,35 +78,6 @@ UCANs (and other forms of PKI) depend on the ambient authority of the owner of e 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. - -``` -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ │ │ │ │ │ -│ │ │ ┌─────────┐ │ │ │ -│ │ │ │ Bob's │ │ │ │ -│ │ │ │ Photo │ │ │ │ -│ │ │ │ Gallery │ │ │ │ -│ │ │ └─────────┘ │ │ │ -│ │ │ │ │ │ -│ Alice's │ │ Bob's │ │ Carol's │ -│ Stuff │ │ Stuff │ │ Stuff │ -│ │ │ │ │ │ -│ ┌───────┼───┼─────────────┼───┼──┐ │ -│ │ │ │ │ │ │ │ -│ │ │ │ ┌───┼───┼──┼────────┐ │ -│ │ │ │ Alice's │ │ │ │ │ │ -│ │ │ │ Music │ │ │ │Carol's │ │ -│ │ │ │ Player │ │ │ │ Game │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ │ └───┼───┼──┼────────┘ │ -│ │ │ │ │ │ │ │ -│ └───────┼───┼─────────────┼───┼──┘ │ -│ │ │ │ │ │ -└─────────────┘ └─────────────┘ └─────────────┘ -``` # 2 Terminology @@ -131,6 +125,7 @@ The same resource MAY be addressed with several URI formats. For instance, a dat ## 2.3 Ability + Abilities describe the verb portion of the capability: an ability that can be performed on a resource. For instance, the standard HTTP methods such as `GET`, `PUT`, and `POST` would be possible `can` values for an `http` resource. While arbitrary semantics MAY be described, they MUST apply to the target resource. For instance, it does not make sense to apply `msg/send` to a typical file system. Abilities MAY be organized in a hierarchy with enums. 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 potencies this way allows for adding more options over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. @@ -141,18 +136,20 @@ There MUST be at least one path segment as a namespace. For example, `http/put` The only reserved ability MUST be the un-namespaced [`"*"` (top)][top ability], which MUST be allowed on any resource. +##### FIXME operation + ## 2.4 Caveats Capabilities MAY define additional optional or required fields specific to their use case in the caveat fields. This field is OPTIONAL in the general case, but MAY be REQUIRED by particular capability types that require this information to validate. Caveats MAY function as an "escape hatch" for when a use case is not fully captured by the resource and ability fields. Caveats can be read as "on the condition that `` holds". ## 2.5 Capability -A capability is the association of an "ability" to a "resource": `resource x ability x caveats`. +A capability is the association of an "ability" to a "resource": `resource × ability × caveats`. The resource and ability fields are REQUIRED. Any non-normative extensions are OPTIONAL. ``` -{ $RESOURCE: { $ABILITY: [ $CAVEATS ] } } +{ $RESOURCE: { $ABILITY: $CAVEATS } } ``` ### 2.5.1 Examples @@ -160,7 +157,7 @@ The resource and ability fields are REQUIRED. Any non-normative extensions are O ``` json { "example://example.com/public/photos/": { - "crud/read": [{}], + "crud/read": [[{}]], "crud/delete": [ { "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" @@ -168,23 +165,29 @@ The resource and ability fields are REQUIRED. Any non-normative extensions are O ] }, "example://example.com/private/84MZ7aqwKn7sNiMGsSbaxsEa6EPnQLoKYbXByxNBrCEr": { - "wnfs/append": [{}] + "wnfs/append": [[{}]] }, "mailto:username@example.com": { - "msg/send": [{}], + "msg/send": [[{}]], "msg/receive": [ - { - "max_count": 5, - "templates": [ - "newsletter", - "marketing" - ] - } + [ + { + "max_count": 5, + "templates": [ + "newsletter", + "marketing" + ] + } + ] ] } } ``` +### FIXME + +MOve sections about RES, ABY, CAV here? + ## 2.6 Authority The set of capabilities delegated by a UCAN is called its "authority." This functions as a declarative description of delegated abilities. @@ -197,11 +200,11 @@ Merging capability authorities MUST follow set semantics, where the result inclu │ │ │ ┌────────────────┼───┐ │ │ │ │ │ Resource B │ │ -│ │ │ │ │ BxZ -│ │ │ X │ ├─── Capability +│ │ │ │ │ B×Z +│ │ │ × │ ├─── Capability │ Resource A │ │ │ │ │ │ │ Ability Z │ │ -│ X │ │ │ │ +│ × │ │ │ │ │ │ │ │ │ │ Ability Y │ │ │ │ │ └───┼───────────────┘ ─┘ @@ -212,11 +215,11 @@ Merging capability authorities MUST follow set semantics, where the result inclu └──────────────────┬─────────────────┘ │ - AxY U BxZ - authority + A×Y U B×Z + Authority ``` -The capability authority is the total rights of the authorization space down to the relevant volume of authorizations. Individual capabilities MAY overlap; the authority is the union. Except for [rights amplification], every unique delegation MUST have equal or narrower capabilities from their delegator. Inside this content space, you can draw a boundary around some resource(s) (their type, identifiers, and paths or children) and their capabilities. +Authority is a set of rights space down to the relevant volume of authorizations. Individual capabilities MAY overlap; the authority is the union. Except for [rights amplification], every unique delegation MUST have equal or narrower capabilities from their delegator. Inside this content space, you can draw a boundary around some resource(s) (their type, identifiers, and paths or children) and their capabilities. For example, given the following authorities against a WebNative filesystem, they can be merged as follows: @@ -226,25 +229,25 @@ For example, given the following authorities against a WebNative filesystem, the AuthorityA = { "wnfs://alice.example.com/pictures/": { - "wnfs/append": [{}] + "wnfs/append": [[{}]] } } AuthorityB = { "wnfs://alice.example.com/pictures/vacation/": { - "wnfs/append": [{}] + "wnfs/append": [[{}]] }, "wnfs://alice.example.com/pictures/vacation/hawaii/": { - "wnfs/overwrite": [{}] + "wnfs/overwrite": [[{}]] } } merge(AuthorityA, AuthorityB) == { "wnfs://alice.example.com/pictures/": { - "wnfs/append": [{}], + "wnfs/append": [[{}]], }, "wnfs://alice.example.com/pictures/vacation/hawaii": { - "wnfs/overwrite": [{}] + "wnfs/overwrite": [[{}]] } // Note that ("/pictures/vacation/" x append) has become redundant, being contained in ("/pictures/" x append) } @@ -269,7 +272,7 @@ Attenuation is the process of constraining the capabilities in a delegation chai { "example://example.com/public/photos/": { - "crud/read": [{}], + "crud/read": [[{}]], "crud/delete": [ { "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" @@ -277,7 +280,7 @@ Attenuation is the process of constraining the capabilities in a delegation chai ] }, "mailto:username@example.com": { - "msg/send": [{}], + "msg/send": [[{}]], "msg/receive": [ { "max_count": 5, @@ -290,45 +293,72 @@ Attenuation is the process of constraining the capabilities in a delegation chai } } - // Example proof capabilities { "example://example.com/public/photos/": { - "crud/read": [{}], - "crud/delete": [{}], // Proof is (correctly) broader than claimed + "crud/read": [[{}]], + "crud/delete": [[{}]], // Proof is (correctly) broader than claimed }, "mailto:username@example.com": { - "msg/send": [{}], // Proof is (correctly) broader than claimed + "msg/send": [[{}]], // Proof is (correctly) broader than claimed "msg/receive": [ - { - "max_count": 5, - "templates": [ - "newsletter", - "marketing" - ] - } + [ + { + "max_count": 5, + "templates": [ + "newsletter", + "marketing" + ] + } + ] ] }, "dns:example.com": { // Not delegated, so no problem "crud/create": [ - {"type": "A"}, - {"type": "CNAME"}, - {"type": "TXT"} + [{"type": "A"}], + [{"type": "CNAME"}], + [{"type": "TXT"}] + ] + } +} +``` + +FIXME +alteranate caveat mechanism could omit `[{}]`, and use `[{a}, {b}]` when there's no `or`. + +e.g. + +``` json +{ + "https://example.com/blog": { + "crud/create": {}, + "crud/destroy": {"a": 0} + "crud/read": [ + {"a": 1}, + {"b": 2} + ], + "crud/update": [ + [ + {"a": 3}, + {"a": 4} + ], + {"a": 5} ] } } ``` -## 2.9 Revocation -Revocation is the act of invalidating a UCAN after the fact, outside of the limitations placed on it by the UCAN's fields (such as its expiry). -In the case of UCAN, this MUST be done by a proof's issuer DID. For more on the exact mechanism, see the [UCAN Revocation] spec. -## 2.10 Invocation -UCANs are used to delegate capabilities between DID-holding agents, eventually terminating in an "invocation" of those capabilities. Invocation is when the capability is exercised to perform some task on a resource. Invocation has its [own specification][UCAN Invocation]. + + + + + + ## 2.11 Time @@ -377,17 +407,16 @@ Note that the JWT `"alg": "none"` option MUST NOT be supported. The lack of sign The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Description | Required | -|-------|------------------------------|--------------------------------------------------------|----------| -| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | -| `iss` | `String` | Issuer DID (sender) | Yes | -| `aud` | `String` | Audience DID (receiver) | Yes | -| `nbf` | `Integer` | Not Before UTC Unix Timestamp in seconds (valid from) | No | -| `exp` | `Integer \| null` | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | -| `nnc` | `String` | Nonce | Yes | -| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | -| `cap` | `{URI: {Ability: [Object]}}` | Capabilities | Yes | -| `prf` | `[CID]` | Proof of delegation (hash-linked UCANs) | No | +| Field | Type | Description | Required | +|-------|-------------------------------------------|--------------------------------------------------------|----------| +| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | +| `iss` | `String` | Issuer DID (sender) | Yes | +| `aud` | `String` | Audience DID (receiver) | Yes | +| `nbf` | `Integer` (53-bits[^js-num-size]) | Not Before UTC Unix Timestamp in seconds (valid from) | No | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | +| `nnc` | `String` | Nonce | Yes | +| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | +| `cap` | `{URI: {Ability: Caveat}}` | Capabilities | Yes | ### 3.2.1 Version @@ -453,7 +482,7 @@ Keeping the window of validity as short as possible is RECOMMENDED. Limiting the ### 3.2.4 Nonce -The OPTIONAL nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. +The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. The recommeneded size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's cryptosuite. @@ -493,7 +522,7 @@ The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowled Capabilities MUST be presented as a map. This map is REQUIRED but MAY be empty. This map MUST contain some or none of the following: -1. A strict subset (attenuation) of the capability authority from the `prf` field +1. A strict subset (attenuation) of the capability authority from the next direct `prf` field 2. Capabilities composed from multiple proofs (see [rights amplification]) 3. Capabilities originated by the `iss` DID (i.e. by parenthood) @@ -570,34 +599,10 @@ Note that for consistency in this syntax, the empty array MUST be equivalent to ### 3.2.7 Proof of Delegation -Attenuations MUST be satisfied by matching the attenuated capability to a proof in the [`prf` array][prf field]. +Attenuations MUST be satisfied by matching the attenuated capability to a proof in the invocation's [`prf` array][invocation prf]. Proofs MUST be resolvable by the recipient. A proof MAY be left unresolvable if it is not used as support for the top-level UCAN's capability chain. The exact format MUST be defined in the relevant transport specification. Some examples of possible formats include: a JSON object payload delivered with the UCAN, a federated HTTP endpoint, a DHT, or a shared database. -#### 3.2.7.1 `prf` Field - -The `prf` field MUST contain the [content address][content identifiers] of UCAN proofs (the "inputs" of a UCAN). - -#### 3.2.7.2 Examples - -``` json -"prf": [ - "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze", - "bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva" -] -``` - -Which in a JSON representation would resolve to the following table: - -```json -{ - "bafkreiemaanh3kxqchhcdx3yckeb3xvmboztptlgtmnu5jp63bvymxtlva": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", - "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" -} -``` - -For more on this representation, please refer to [canonical collections]. - # 4 Reserved Namespaces ## 4.1 `ucan` @@ -849,7 +854,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [canonical collections]: #71-canonical-json-collection [content identifiers]: #65-content-identifiers [delegation]: #51-ucan-delegation -[prf field]: #3271-prf-field +[invocation prf]: FIXME [replay attack prevention]: #93-replay-attack-prevention [revocation]: #66-revocation [rights amplification]: #64-rights-amplification From 97984dfb14cccbd9d7f281f91280b6d87ea528d8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 6 Oct 2023 16:54:18 -0700 Subject: [PATCH 018/134] DNF --- README.md | 132 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 3207e20..2bdedaf 100644 --- a/README.md +++ b/README.md @@ -547,35 +547,95 @@ Resources in the capabilities map MAY overlap. For example, the following MAY co Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. -#### 3.2.6.3 Caveat Array +#### 3.2.6.3 Caveats + + +FIXME add _so much_ clarification +- Semantics defined by resource & OG issuer +- + + + Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST prevent invocation otherwise. Caveats MUST be formatted as objects. On validation, the caveat array MUST be treated as a logically disjunct (an "OR", NOT an "and"). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: -```json + + + +``` json { - "dns:example.com?TYPE=TXT": { - "crud/create": [{}] + "dns:example.com": { + "crud/create": {} }, "https://example.com/blog": { - "crud/read": [{}], + "crud/read": {"status": "published"}, + "crud/create": [ + {"status": "draft"}, + {"status": "published", "day-of-week": "monday"} + ], "crud/update": [ - {"status": "draft"}, - {"status": "published", "day-of-week": "Monday"} // only publish on Mondays + [ + {"status": "published"} + {"tag": "breaking"}, + {"tag": "news"}, + ], + {"status": "draft"} ] } } ``` - + The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. -| Resource | Ability | Caveat | -|----------------------------|---------------|---------------------------------------------------| -| `dns:example.com?TYPE=TXT` | `crud/create` | Always | -| `https://example.com/blog` | `crud/read` | Always | -| `https://example.com/blog` | `crud/update` | `{status: "draft"}` | -| `https://example.com/blog` | `crud/update` | `{status: "published", "day-of-week": "monday"}` | +| Resource | Ability | Caveat | +|----------------------------|---------------|-----------------------------------------------------------------| +| `dns:example.com` | `crud/create` | Always | +| `https://example.com/blog` | `crud/read` | Published posts | +| `https://example.com/blog` | `crud/create` | Draft posts | +| `https://example.com/blog` | `crud/create` | Published posts on Mondays | +| `https://example.com/blog` | `crud/update` | Draft posts | +| `https://example.com/blog` | `crud/update` | Published posts that are tagged with both `breaking` and `news` | + +### XXXX.XXXXX Normal Form + +Note that all caveats need to be understable to th eexecitor + +All of the above can be validly expressed in [DNF]. + +``` json +{ + "dns:example.com": { + "crud/create": [[{}]] + }, + "https://example.com/blog": { + "crud/read": [[{"status": "published"}]], + "crud/create": [ + [{"status": "draft"}], + [{"status": "published", "day-of-week": "monday"}] + ], + "crud/update": [ + [ + {"status": "published"} + {"tag": "breaking"}, + {"tag": "news"}, + ], + [{"status": "draft"}] + ] + } +} +``` + + + + + + + + + + The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. @@ -592,10 +652,10 @@ The caveat array SHOULD NOT be empty, as an empty array means "in no case" (whic Note that for consistency in this syntax, the empty array MUST be equivalent to disallowing the capability. Conversely, an empty object MUST be treated as "no caveats". -| Proof Caveats | Comment | -|---------------|---------------------------------------------------------------| -| `[]` | No capabilities | -| `[{}]` | Full capabilities for this resource/ability pair (no caveats) | +| Proof Caveats | Comment | +|------------------------|---------------------------------------------------------------| +| `[]` | No capabilities | +| `{}`, `[{}]`, `[[{}]]` | Full capabilities for this resource/ability pair (no caveats) | ### 3.2.7 Proof of Delegation @@ -786,42 +846,6 @@ Token resolution is transport specific. The exact format is left to the relevant Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics] of UCAN. -# 7 Implementation Recommendations - -## 7.1 UCAN Store - -A validator MAY keep a local store of UCANs that it has received. UCANs are immutable but also time-bound so that this store MAY evict expired or revoked UCANs. - -This store MAY be indexed by CID (content addressing). Multiple indices built on top of this store MAY be used to improve capability search or selection performance. - -## 7.2 Memoized Validation - -Aside from revocation, capability validation is idempotent. Marking a CID (or capability index inside that CID) as valid acts as memoization, obviating the need to check the entire structure on every validation. This extends to distinct UCANs that share a proof: if the proof was previously reviewed and is not revoked, it is RECOMMENDED to consider it valid immediately. - -Revocation is irreversible. Suppose the validator learns of revocation by UCAN CID or issuer DID. In that case, the UCAN and all of its derivatives in such a cache MUST be marked as invalid, and all validations immediately fail without needing to walk the entire structure. - -## 7.3 Replay Attack Prevention - -Replay attack prevention is REQUIRED ([Token Uniqueness]). The exact strategy is left to the implementer. One simple strategy is maintaining a set of previously seen CIDs. This MAY be the same structure as a validated UCAN memoization table (if one exists in the implementation). - -It is RECOMMENDED that the structure have a secondary index referencing the token expiry field. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on. - -# 8 Prior Art - -[SPKI/SDSI] is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written. - -[ZCAP-LD] is closely related to UCAN. The primary differences are in formatting, addressing by URL instead of CID, the mechanism of separating invocation from authorization, and single versus multiple proofs. - -[CACAO] is a translation of many of these ideas to a cross-blockchain invocation model. It contains the same basic concepts but is aimed at small messages and identities that are rooted in mutable documents rooted on a blockchain and lacks the ability to subdelegate capabilities. - -[Local-First Auth] uses CRDT-based ACLs and key lockboxes for role-based signatures. This is a non-certificate-based approach, instead of relying on the CRDT and signed data to build up a list of roles and members. It does have a very friendly invitation certificate mechanism in [Seitan token exchange]. It is also straightforward to see which users have access to what, avoiding the confinement problem seen in many decentralized auth systems. - -[Macaroon] is a MAC-based capability and cookie system aimed at distributing authority across services in a trusted network (typically in the context of a Cloud). By not relying on asymmetric signatures, Macaroons achieve excellent space savings and performance, given that the MAC can be checked against the relevant services during invocation. The authority is rooted in an originating server rather than with an end-user. - -[Biscuit] uses Datalog to describe capabilities. It has a specialized format but is otherwise in line with UCAN. - -[Verifiable credentials] are a solution for data about people or organizations. However, they are aimed at a slightly different problem: asserting attributes about the holder of a DID, including things like work history, age, and membership. - # 9. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. From 86aece34df79ba32e1117b68fae0b3465ef3a3f4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 10 Oct 2023 22:02:37 -0700 Subject: [PATCH 019/134] DNFify --- README.md | 473 +++++++++++++++--------------------------------------- 1 file changed, 126 insertions(+), 347 deletions(-) diff --git a/README.md b/README.md index 2bdedaf..a92f129 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ TODO - Remove general motivation section, narrow to delegation +- Consider batch signatures for batch use cases + - Cature as an invocation? Seems like overkill + - Define `alg: "batch/RS256"`, `alg: "batch/EdDSA"`, etc? No +- Scope all to DIDs @@ -46,309 +50,9 @@ UCAN Delegation is a component of the [UCAN] specification. This specification d ## 1.1 Motivation -Since at least the release of Unix, access control lists (ACLs) have been the most popular form of digital authorization, where a list of what each user is allowed to do is maintained on the resource. ACLs have been a successful model suited to architectures where persistent access to a single list is viable. ACLs require that rules are sufficiently well specified, such as in a centralized database with rules covering all possible permutations of rights. - -With increasing interconnectivity between machines becoming commonplace, authorization needs to scale to meet the load demands of distributed systems while providing partition tolerance. However, it is not always practical to maintain a single central authorization source. Even when copies of the authorization list are distributed to the relevant servers, latency and partitions introduce troublesome challenges with conflicting updates, to say nothing of storage requirements. - -A large portion of personal information now also moves through connected systems. As a result, data privacy is a prominent theme when considering the design of modern applications, to the point of being legislated in parts of the world. - -Ahead-of-time coordination is often a barrier to development in many projects. Flexibility to define specialized authorization semantics for resources and the ability to integrate with external systems trustlessly are essential as the number of autonomous, specialized, and coordinating applications increases. - -Many high-value applications run in hostile environments. In recognition of this, many vendors now include public key functionality, such as [non-extractable keys in browsers][browser api crypto key], [certificate systems for external keys][fido], and [secure hardware enclave]s in widespread consumer devices. - -Two related models that work exceptionally well in the above context are Simple Public Key Infrastructure ([SPKI][spki rfc]) and object capabilities ([OCAP]). Since offline operation and self-verifiability are two requirements, UCAN adopts an approach closely related to SPKI. UCANs follow the "capabilities as certificates" model, with extensions for revocation and stateful capabilities. - -## 1.2 Intuition - -By analogy, ACLs are like a bouncer at an exclusive event. This bouncer has a list attendees allowed in and which of those are VIPs that get extra access. The attendees show their government-issued ID and are accepted or rejected. - -If there are many such events at many venues, the organizers need to coordinate ahead of time, denials need to be synchronized, and attendees need to show their ID cards to many bouncers. The likelihood of the bouncer letting in the wrong person due to synchronization lag or confusion by someone sharing a name is nonzero. - -UCANs work more like [movie tickets][caps as keys] or a festival pass between multiple venues. No one needs to check your ID; who you are is irrelevant. For example, if you have a ticket to see Citizen Kane, you are admitted to Theater 3. If you cannot attend an event, you can hand this ticket to a friend who wants to see the film instead, and there is no coordination required with the theater ahead of time. However, if the theater needs to cancel tickets for some reason, they need a way of uniquely identifying them and sharing this information between them. - -The above analogies illustrate several significant tradeoffs between these systems but are only accurate enough to build intuition. A good resource for a more thorough presentation of these tradeoffs is [Capability Myths Demolished]. In this framework, UCAN approximates SPKI with some dynamic features. - -## 1.3 Security Considerations - -Each UCAN includes a constructive set of assertions of what it is allowed to do. Note that this is not a predicate: it is a positive assertion of rights. "Proofs" are positive evidence (elsewhere called "witnesses") of the possession of rights. They are cryptographically signed data showing that the UCAN issuer either owns this or that it was delegated to them by the root owner. - -This signature chain is the root of trust. Private keys themselves SHOULD NOT move from one context to another: this is what the delegation mechanism provides: "sharing authority without sharing keys." - -UCANs (and other forms of PKI) depend on the ambient authority of the owner of each resource. This means that the invoking agent must be able to verify the root ownership at decision time. The rest of the chain in-between is self-certifying. - -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. - # 2 Terminology -## 2.1 Roles - -There are several roles that an agent MAY assume: - -| Name | Description | -| --------- | ------------------------------------------------------------------------------------------------ | -| Agent | The general class of entities and principals that interact with a UCAN | -| Validator | Any agent that interprets a UCAN to determine that it is valid, and which capabilities it grants | -| Principal | An agent identified by DID (listed in a UCAN's `iss` or `aud` field) | -| Audience | The principal delegated to in the current UCAN. Listed in the `aud` field | -| Issuer | The signer of the current UCAN. Listed in the `iss` field | -| Revoker | The issuer listed in a proof chain that revokes a UCAN | -| Owner | The root issuer of a capability, who has some proof that they fully control the resource | - -``` mermaid -flowchart TD - subgraph Agent - subgraph Principal - direction TB - - subgraph Issuer - direction TB - - Owner - Revoker - end - - Audience - end - - Validator - end -``` - -## 2.2 Resource - -A resource is some data or process that has an address. It can be anything from a row in a database, a user account, storage quota, email address, etc. - -A resource describes the noun of a capability. The resource pointer MUST be provided in [URI] format. Arbitrary and custom URIs MAY be used, provided that the intended recipient can decode the URI. The URI is merely a unique identifier to describe the pointer to — and within — a resource. - -The same resource MAY be addressed with several URI formats. For instance, a database may have an address at the level of direct memory with `file`, via `sqldb` to gain access to SQL semantics, `http` to use web addressing, and `dnslink` to use Merkle DAGs inside DNS `TXT` records. - -## 2.3 Ability - - -Abilities describe the verb portion of the capability: an ability that can be performed on a resource. For instance, the standard HTTP methods such as `GET`, `PUT`, and `POST` would be possible `can` values for an `http` resource. While arbitrary semantics MAY be described, they MUST apply to the target resource. For instance, it does not make sense to apply `msg/send` to a typical file system. - -Abilities MAY be organized in a hierarchy with enums. 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 potencies this way allows for adding more options over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. - -Abilities MUST NOT be case sensitive. For example, `http/post`, `http/POST`, `HTTP/post`, `HTTP/POST`, and `hTtP/pOsT` MUST all mean the same ability. - -There MUST be at least one path segment as a namespace. For example, `http/put` and `db/put` MUST be treated as unique from each other. - -The only reserved ability MUST be the un-namespaced [`"*"` (top)][top ability], which MUST be allowed on any resource. - -##### FIXME operation - -## 2.4 Caveats - -Capabilities MAY define additional optional or required fields specific to their use case in the caveat fields. This field is OPTIONAL in the general case, but MAY be REQUIRED by particular capability types that require this information to validate. Caveats MAY function as an "escape hatch" for when a use case is not fully captured by the resource and ability fields. Caveats can be read as "on the condition that `` holds". - -## 2.5 Capability - -A capability is the association of an "ability" to a "resource": `resource × ability × caveats`. - -The resource and ability fields are REQUIRED. Any non-normative extensions are OPTIONAL. - -``` -{ $RESOURCE: { $ABILITY: $CAVEATS } } -``` - -### 2.5.1 Examples - -``` json -{ - "example://example.com/public/photos/": { - "crud/read": [[{}]], - "crud/delete": [ - { - "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" - } - ] - }, - "example://example.com/private/84MZ7aqwKn7sNiMGsSbaxsEa6EPnQLoKYbXByxNBrCEr": { - "wnfs/append": [[{}]] - }, - "mailto:username@example.com": { - "msg/send": [[{}]], - "msg/receive": [ - [ - { - "max_count": 5, - "templates": [ - "newsletter", - "marketing" - ] - } - ] - ] - } -} -``` - -### FIXME - -MOve sections about RES, ABY, CAV here? - -## 2.6 Authority - -The set of capabilities delegated by a UCAN is called its "authority." This functions as a declarative description of delegated abilities. - -Merging capability authorities MUST follow set semantics, where the result includes all capabilities from the input authorities. Since broader capabilities automatically include narrower ones, this process is always additive. Capability authorities can be combined in any order, with the result always being at least as broad as each of the original authorities. - -``` plaintext - ┌───────────────────┐ ─┐ - │ │ │ - │ │ │ -┌────────────────┼───┐ │ │ -│ │ │ Resource B │ │ -│ │ │ │ │ B×Z -│ │ │ × │ ├─── Capability -│ Resource A │ │ │ │ -│ │ │ Ability Z │ │ -│ × │ │ │ │ -│ │ │ │ │ -│ Ability Y │ │ │ │ -│ └───┼───────────────┘ ─┘ -│ │ -│ │ -└────────────────────┘ - -└──────────────────┬─────────────────┘ - │ - - A×Y U B×Z - Authority -``` - -Authority is a set of rights space down to the relevant volume of authorizations. Individual capabilities MAY overlap; the authority is the union. Except for [rights amplification], every unique delegation MUST have equal or narrower capabilities from their delegator. Inside this content space, you can draw a boundary around some resource(s) (their type, identifiers, and paths or children) and their capabilities. - -For example, given the following authorities against a WebNative filesystem, they can be merged as follows: - -```js -// "wnfs" abilities: -// fetch < append < overwrite < superuser < top - -AuthorityA = { - "wnfs://alice.example.com/pictures/": { - "wnfs/append": [[{}]] - } -} - -AuthorityB = { - "wnfs://alice.example.com/pictures/vacation/": { - "wnfs/append": [[{}]] - }, - "wnfs://alice.example.com/pictures/vacation/hawaii/": { - "wnfs/overwrite": [[{}]] - } -} - -merge(AuthorityA, AuthorityB) == { - "wnfs://alice.example.com/pictures/": { - "wnfs/append": [[{}]], - }, - "wnfs://alice.example.com/pictures/vacation/hawaii": { - "wnfs/overwrite": [[{}]] - } - // Note that ("/pictures/vacation/" x append) has become redundant, being contained in ("/pictures/" x append) -} -``` - -## 2.7 Delegation - -Delegation is the act of granting another principal (the delegate) the capability to use a resource that another has (the delegator). A constructive "proof" acts as the authorization proof for a delegation. Proofs are either the owning principal's signature or a UCAN with access to that capability in its authority. - -Each direct delegation leaves the ability at the same level or diminishes it. The only exception is in "rights amplification," where a delegation MAY be proven by one-or-more proofs of different types if part of the resource's semantics. - -Note that delegation is a separate concept from [invocation]. Delegation is the act of granting a capability to another, not the act of using it (invocation), which has additional requirements. - -## 2.8 Attenuation - -Attenuation is the process of constraining the capabilities in a delegation chain. - -### 2.8.1 Examples - -``` json -// Example claimed capabilities - -{ - "example://example.com/public/photos/": { - "crud/read": [[{}]], - "crud/delete": [ - { - "matching": "/(?i)(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)/" - } - ] - }, - "mailto:username@example.com": { - "msg/send": [[{}]], - "msg/receive": [ - { - "max_count": 5, - "templates": [ - "newsletter", - "marketing" - ] - } - ] - } -} - -// Example proof capabilities - -{ - "example://example.com/public/photos/": { - "crud/read": [[{}]], - "crud/delete": [[{}]], // Proof is (correctly) broader than claimed - }, - "mailto:username@example.com": { - "msg/send": [[{}]], // Proof is (correctly) broader than claimed - "msg/receive": [ - [ - { - "max_count": 5, - "templates": [ - "newsletter", - "marketing" - ] - } - ] - ] - }, - "dns:example.com": { // Not delegated, so no problem - "crud/create": [ - [{"type": "A"}], - [{"type": "CNAME"}], - [{"type": "TXT"}] - ] - } -} -``` - -FIXME -alteranate caveat mechanism could omit `[{}]`, and use `[{a}, {b}]` when there's no `or`. - -e.g. - -``` json -{ - "https://example.com/blog": { - "crud/create": {}, - "crud/destroy": {"a": 0} - "crud/read": [ - {"a": 1}, - {"b": 2} - ], - "crud/update": [ - [ - {"a": 3}, - {"a": 4} - ], - {"a": 5} - ] - } -} -``` - @@ -529,18 +233,7 @@ This map MUST contain some or none of the following: The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. ``` -{ $RESOURCE: { $ABILITY: [ ...$CAVEATS ] } } -``` - -#### 3.2.6.1 Resource - -Resources MUST be unique and given as [URI]s. - -Resources in the capabilities map MAY overlap. For example, the following MAY coexist as top-level keys in a capabilities map: - -```json -"https://example.com", -"https://example.com/blog" +{ $SUBJECT: { $ABILITY: [ ...$CAVEATS ] } } ``` #### 3.2.6.2 Abilities @@ -556,6 +249,26 @@ FIXME add _so much_ clarification + + + + + Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST prevent invocation otherwise. Caveats MUST be formatted as objects. @@ -566,22 +279,31 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" ``` json { - "dns:example.com": { - "crud/create": {} + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", + "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": { + "crud/create": { + "uri": "dns:example.com", + "record": "TXT" + } }, - "https://example.com/blog": { - "crud/read": {"status": "published"}, - "crud/create": [ - {"status": "draft"}, - {"status": "published", "day-of-week": "monday"} - ], + "did:web:example.com": { + "crud/read": { + "uri": "https://blog.example.com", + "status": "published" + }, "crud/update": [ + { + "uri": "https://example.com/newsletter/", + "status": "draft" + }, [ - {"status": "published"} - {"tag": "breaking"}, - {"tag": "news"}, - ], - {"status": "draft"} + { + "uri": "https://blog.example.com", + "status": "published" + "tag": "news", + } + { "tag": "breaking" }, + ] ] } } @@ -589,14 +311,14 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. -| Resource | Ability | Caveat | -|----------------------------|---------------|-----------------------------------------------------------------| -| `dns:example.com` | `crud/create` | Always | -| `https://example.com/blog` | `crud/read` | Published posts | -| `https://example.com/blog` | `crud/create` | Draft posts | -| `https://example.com/blog` | `crud/create` | Published posts on Mondays | -| `https://example.com/blog` | `crud/update` | Draft posts | -| `https://example.com/blog` | `crud/update` | Published posts that are tagged with both `breaking` and `news` | +| Subject | Ability | Caveat | +|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| +| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | +| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | +| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | +| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | +| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | + ### XXXX.XXXXX Normal Form @@ -606,22 +328,35 @@ All of the above can be validly expressed in [DNF]. ``` json { - "dns:example.com": { - "crud/create": [[{}]] + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "ucan/*": [[{}]] + }, + "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": { + "crud/create": [ FIXME: ambiguity if you only pass crud/create, then later they add a resource. You want to know *what* you can update + [ + { "uri": "dns:example.com" }, + { "record": "TXT" } + ] + ] }, - "https://example.com/blog": { - "crud/read": [[{"status": "published"}]], - "crud/create": [ - [{"status": "draft"}], - [{"status": "published", "day-of-week": "monday"}] + "did:web:example.com": { + "crud/read": [ + [ + { "uri": "https://blog.example.com" }, + { "status": "published" } + ] ], "crud/update": [ [ - {"status": "published"} - {"tag": "breaking"}, - {"tag": "news"}, + { "uri": "https://example.com/newsletter/" }, + { "status": "draft" } ], - [{"status": "draft"}] + [ + { "uri": "https://blog.example.com" }, + { "status": "published" }, + { "tag": "news" }, + { "tag": "breaking" } + ] ] } } @@ -659,7 +394,7 @@ Note that for consistency in this syntax, the empty array MUST be equivalent to ### 3.2.7 Proof of Delegation -Attenuations MUST be satisfied by matching the attenuated capability to a proof in the invocation's [`prf` array][invocation prf]. +Attenuations MUST be satisfied by matching the attenuated [FIXME: in invocation] capability to a proof in the invocation's [`prf` array][invocation prf]. Proofs MUST be resolvable by the recipient. A proof MAY be left unresolvable if it is not used as support for the top-level UCAN's capability chain. The exact format MUST be defined in the relevant transport specification. Some examples of possible formats include: a JSON object payload delivered with the UCAN, a federated HTTP endpoint, a DHT, or a shared database. @@ -815,9 +550,11 @@ Each capability MUST either be originated by the issuer (root capability, or "pa Except for rights amplification (below), each capability delegation MUST have equal or narrower capabilities from its proofs. The time bounds MUST also be equal to or contained inside the time bounds of the proof's time bounds. This lowering of rights at each delegation is called "attenuation." -## 5.4 Rights Amplification -Some capabilities are more than the sum of their parts. The canonical example is a can of soup and a can opener. You need both to access the soup inside the can, but the can opener may come from a completely separate source than the can of soup. Such semantics MAY be implemented in UCAN capabilities. This means that validating particular capabilities MAY require more than one direct proof. The relevant proofs MAY be of a different resource and ability from the amplified capability. The delegated capability MUST have this behavior in its semantics, even if the proofs do not. + + + +FIXME removed section, fix numbering ## 5.5 Content Identifiers @@ -962,3 +699,45 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [time definition]: https://en.wikipedia.org/wiki/Temporal_database [ucan-uri]: https://github.com/ucan-wg/ucan-uri [ucan.xyz]: https://ucan.xyz + + + + + + + + + + + + + +FIXME move to own spec? + +# 7. Collections + +FIXME update exmaple to latest format ...or break out into own spec :shrug: + +UCANs are indexed by their hash — often called their ["content address"][content addressable storage]. UCANs MUST be addressable as [CIDv1]. Use of a [canonical CID] is RECOMMENDED. + +Content addressing the proofs has multiple advantages over inlining tokens, including: +* Avoids re-encoding deeply nested proofs as Base64 many times (and the associated size increase) +* Canonical signature +* Enables only transmitting the relevant proofs + +Multiple UCANs in a single request MAY be collected into one table. It is RECOMMENDED that these be indexed by CID. The [canonical JSON representation][canonical collections] (below) MUST be supported. Implementations MAY include more formats, for example to optimize for a particular transport. Transports MAY map their collection to this collection format. + +### 7.1 Canonical JSON Collection + +The canonical JSON representation is an key-value object, mapping UCAN content identifiers to their fully-encoded base64url strings. A root "entry point" (if one exists) MUST be indexed by the slash `/` character. + +#### 7.1.1 Example + + +``` json +{ + "/": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", + "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" +} +``` + From bfcb10952ac4d04330fd586b68d05d346762d814 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 11 Oct 2023 13:33:52 -0700 Subject: [PATCH 020/134] Add a bunch of examples, reworking for clarity --- README.md | 341 +++++++++++++++++++----------------------------------- 1 file changed, 117 insertions(+), 224 deletions(-) diff --git a/README.md b/README.md index a92f129..c8d07a4 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,8 @@ -TODO -- Remove general motivation section, narrow to delegation -- Consider batch signatures for batch use cases - - Cature as an invocation? Seems like overkill - - Define `alg: "batch/RS256"`, `alg: "batch/EdDSA"`, etc? No -- Scope all to DIDs - - - - - - - - - - - +TODOs +- externally owned resources ## Editors @@ -44,59 +29,46 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 0 Abstract -UCAN Delegation is a component of the [UCAN] specification. This specification describes the semantics and serialization format for [UCAN] delegation between principals. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities] by extending the familiar [JWT] structure. UCANs achieve public verifiability with chained certificates and [decentralized identifiers (DIDs)][DID]. Verifiable chain compression is enabled via [content addressing]. Being encoded with the familiar JWT, UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI][SPKI] for web and native application contexts. +UCAN Delegation is a component of [UCAN]. This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilties, heirarchical authority, and extensible caveats. # 1 Introduction ## 1.1 Motivation +Design goals: -# 2 Terminology - - - - - - - - - - - - -## 2.11 Time - -Time takes on [multiple meanings][time definition] in systems representing facts or knowledge. The senses of the word "time" are given below. - -### 2.11.1 Valid Time Range +- Flexible pathing +- Extensibilty +- Consistency for interop -The period of time that a capability is valid from and until. +# 2 Token Structure -### 2.11.2 Assertion Time +Regardless of how a Delegation is serialized on the wire, the sigature of a UCAN Delegation MUST be over a payload serialized as a [JWT] . -The moment at which a delegation was asserted. This MAY be captured via an `iat` field, but is generally superfluous to capture in the token. "Assertion time" is useful when discussing the lifecycle of a token. - -### 2.11.3 Decision (or Validation) Time - -Decision time is the part of the lifecycle when "a decision" about the token is made. This is typically during validation, but also includes resolving external state (e.g. storage quotas). - -# 3 JWT Structure - -UCANs MUST be formatted as [JWT]s, with additional keys as described in this document. The overall container of a header, claims, and signature remains. Please refer to [RFC 7519][JWT] for more on this format. +The overall container of a header, claims, and signature remain as per [RFC 7519][JWT]. ## 3.1 Header -The header MUST include all of the following fields: +The header is a standard JWT header, and MUST include all of the following fields: + | Field | Type | Description | Required | |-------|----------|--------------------------------|----------| | `alg` | `String` | Signature algorithm | Yes | | `typ` | `String` | Type (MUST be `"JWT"`) | Yes | -The header is a standard JWT header. +### 3.1.1 Algorithms + +It is RECOMMENDED that the following algorithms be supported: -EdDSA, as applied to JOSE (including JWT), is described in [RFC 8037]. +- [RS256] +- [EdDSA][RFC 8037] +- [P-256 ECDSA] -Note that the JWT `"alg": "none"` option MUST NOT be supported. The lack of signature prevents the issuer from being validatable. +All algorithms MUST match the DID principal in the `iss` field. This enforces that the `alg` field MUST be asymmetric (public key cryptography or nonstandard but emerging patterns like smart contract signatures) + +The JWT `"alg": "none"` MUST NOT be supported, as the lack of signature prevents the issuer DID from being validated. + +Symmetric algorithms such as HMACs (e.g. `"alg": "HS256"`) MUST NOT be supported, since they cannot be used to prove control over the issuer DID. ### 3.1.1 Examples @@ -140,6 +112,8 @@ It is RECOMMENDED that the following `did:key` types be supported: - [EdDSA][did:key EdDSA] - [P-256 ECDSA][did:key ECDSA] +Note that every [Subject] MUST correspond to a root delegation issuer. + #### 3.2.2.1 Examples ```json @@ -172,6 +146,8 @@ The `exp` field MUST be set. Following the [principle of least authority][POLA], Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. +Several named points of time in the UCAN lifecycle [can be found in the high level spec]. + [^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. #### 3.2.3.1 Example @@ -192,17 +168,19 @@ The recommeneded size of the nonce differs by key type. In many cases, a random This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`fct`][Facts] field for more. -#### 3.2.4.1 Examples +#### 3.2.4.1 Example ``` json { // ... - "nnc": "1701-D" + "nnc": "NCC-1701-D" } ``` ### 3.2.5 Facts +FIXME not validated in the chain: do what you want! + The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. #### 3.2.5.1 Examples @@ -223,7 +201,7 @@ The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowled ### 3.2.6 Capabilities & Attenuation -Capabilities MUST be presented as a map. This map is REQUIRED but MAY be empty. +Capabilities MUST be presented as a map under the `cap` field. This map is REQUIRED but MAY be empty. This map MUST contain some or none of the following: 1. A strict subset (attenuation) of the capability authority from the next direct `prf` field @@ -232,41 +210,96 @@ This map MUST contain some or none of the following: The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. +The capabilities take this form: + ``` -{ $SUBJECT: { $ABILITY: [ ...$CAVEATS ] } } +{ $SUBJECT: { $ABILITY: $CAVEATS } } ``` -#### 3.2.6.2 Abilities +FIXME example -Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. - -#### 3.2.6.3 Caveats +#### 3.2.6.1 Subject +The Subject MUST be the DID that initiated the delegation chain. -FIXME add _so much_ clarification -- Semantics defined by resource & OG issuer -- +For example: +``` js +{ + // ... + cap: { + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*" + //└───────────────────────────┬────────────────────────────┘ + // SUBJECT + } +} +``` +#### 3.2.6.2 Resource - - +#### 3.2.6.3 Abilities + +Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. + +Abilities MUST be defined by the Subject. While it is correct in this narrow context to think about the Subject as a namespace, + +#### 3.2.6.4 Caveats + +Caveat semantics MUST be defined by the Subject. + +FIXME add _so much_ clarification +- Semantics defined by resource & OG issuer +- push resource into caveats +- optional resource, message passing analogy +- Normal form and compact form @@ -276,7 +309,6 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" - ``` json { "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", @@ -402,7 +434,14 @@ Proofs MUST be resolvable by the recipient. A proof MAY be left unresolvable if ## 4.1 `ucan` -The `ucan` resource and abilty namespace MUST be reserved. Implementation of the [`ucan-uri`] spec is RECOMMENDED. +The `ucan` resource and abilty namespace MUST be reserved. + +### 4.1.1 `ucan/*` + +Support for `ucan/*` is RECOMMENDED. + + +FIXME ## 4.2 "Top" Ability @@ -441,6 +480,8 @@ In concept there is a "bottom" ability ("none" or "void"), but it is not possibl # 5 Validation +FIXME note that validation happens at exection time + Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator MUST NOT reject all capabilities when only one is not understood. If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: @@ -488,6 +529,8 @@ const ensureProofExp = (ucan, proof) => { ## 5.2 Principal Alignment +FIXME move to high level spec + In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. This calculation MUST NOT take into account [DID fragment]s. If present, fragments are only intended to clarify which of a DID's keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired. @@ -540,22 +583,8 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -### 5.2.2 Token Uniqueness - -Each remote invocation MUST be a unique UCAN: for instance using a nonce (`nnc`) or simply a unique expiry. The recipient MUST validate that they have not received the top-level UCAN before. For implementation recommentations, please refer to the [replay attack prevention] section. - -## 5.3 Proof Chaining - -Each capability MUST either be originated by the issuer (root capability, or "parenthood") or have one-or-more proofs in the `prf` field to attest that this issuer is authorized to use that capability ("introduction"). In the introduction case, this check MUST be recursively applied to its proofs until a root proof is found (i.e. issued by the resource owner). - -Except for rights amplification (below), each capability delegation MUST have equal or narrower capabilities from its proofs. The time bounds MUST also be equal to or contained inside the time bounds of the proof's time bounds. This lowering of rights at each delegation is called "attenuation." - - - -FIXME removed section, fix numbering - ## 5.5 Content Identifiers A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. @@ -573,15 +602,6 @@ A canonical CID can be important for some use cases, such as caching and [revoca * [SHA2-256] * [Raw data multicodec] (`0x55`) -# 6 Token Resolution - -Token resolution is transport specific. The exact format is left to the relevant UCAN transport specification. At minimum, such a specification MUST define at least the following: - -1. Request protocol -2. Response protocol -3. Collections format - -Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics] of UCAN. # 9. Acknowledgments @@ -611,133 +631,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Token Uniqueness]: #622-token-uniqueness -[canonical collections]: #71-canonical-json-collection -[content identifiers]: #65-content-identifiers -[delegation]: #51-ucan-delegation -[invocation prf]: FIXME -[replay attack prevention]: #93-replay-attack-prevention -[revocation]: #66-revocation -[rights amplification]: #64-rights-amplification -[token resolution]: #8-token-resolution -[top ability]: #41-top -[Alan Karp]: https://github.com/alanhkarp -[Benjamin Goering]: https://github.com/gobengo -[Biscuit]: https://github.com/biscuit-auth/biscuit/ -[Blaine Cook]: https://github.com/blaine -[Bluesky]: https://blueskyweb.xyz/ -[Brendan O'Brien]: https://github.com/b5 -[Brian Ginsburg]: https://github.com/bgins -[Brooklyn Zelenka]: https://github.com/expede -[CACAO]: https://blog.ceramic.network/capability-based-data-security-on-ceramic/ -[CIDv1]: https://docs.ipfs.io/concepts/content-addressing/#identifier-formats -[Canonical CID]: #651-cid-canonicalization -[Capability Myths Demolished]: https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf -[Christine Lemmer-Webber]: https://github.com/cwebber -[Christopher Joel]: https://github.com/cdata -[DID fragment]: https://www.w3.org/TR/did-core/#fragment -[DID path]: https://www.w3.org/TR/did-core/#path -[DID subject]: https://www.w3.org/TR/did-core/#dfn-did-subjects -[DID]: https://www.w3.org/TR/did-core/ -[Dan Finlay]: https://github.com/danfinlay -[Daniel Holmgren]: https://github.com/dholms -[ECDSA security]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Security -[FIDO]: https://fidoalliance.org/fido-authentication/ -[Fission]: https://fission.codes -[Hugo Dias]: https://github.com/hugomrdias -[IEEE 754]: https://ieeexplore.ieee.org/document/8766229 -[Irakli Gozalishvili]: https://github.com/Gozala -[JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number -[JWT]: https://datatracker.ietf.org/doc/html/rfc7519 -[Juan Caballero]: https://github.com/bumblefudge -[Local-First Auth]: https://github.com/local-first-web/auth -[Macaroon]: https://storage.googleapis.com/pub-tools-public-publication-data/pdf/41892.pdf -[Mark Miller]: https://github.com/erights -[Mikael Rogers]: https://github.com/mikeal/ -[OCAP]: http://erights.org/elib/capability/index.html -[OCapN]: https://github.com/ocapn/ocapn -[POLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege -[Philipp Krüger]: https://github.com/matheus23 -[Protocol Labs]: https://protocol.ai/ -[RFC 2119]: https://datatracker.ietf.org/doc/html/rfc2119 -[RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339 -[RFC 8037]: https://datatracker.ietf.org/doc/html/rfc8037 -[SHA2-256]: https://en.wikipedia.org/wiki/SHA-2 -[SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ -[SPKI]: https://theworld.com/~cme/html/spki.html -[Seitan token exchange]: https://book.keybase.io/docs/teams/seitan -[Steven Vandevelde]: https://github.com/icidasset -[UCAN Invocation]: https://github.com/ucan-wg/invocation -[UCAN Revocation]: https://github.com/ucan-wg/revocation -[URI]: https://www.rfc-editor.org/rfc/rfc3986 -[Verifiable credentials]: https://www.w3.org/2017/vc/WG/ -[W3C]: https://www.w3.org/ -[ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ -[`did:3`]: https://github.com/ceramicnetwork/CIPs/blob/main/CIPs/cip-79.md -[`did:ion`]: https://github.com/decentralized-identity/ion -[`did:key`]: https://w3c-ccg.github.io/did-method-key/ -[base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L12 -[browser api crypto key]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey -[capabilities]: https://en.wikipedia.org/wiki/Object-capability_model -[caps as keys]: http://www.erights.org/elib/capability/duals/myths.html#caps-as-keys -[confinement]: http://www.erights.org/elib/capability/dist-confine.html -[constructive semantics]: https://en.wikipedia.org/wiki/Intuitionistic_logic -[content addressable storage]: https://en.wikipedia.org/wiki/Content-addressable_storage -[content addressing]: https://en.wikipedia.org/wiki/Content-addressable_storage -[dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L104 -[did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 -[did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 -[did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa -[disjunction]: https://en.wikipedia.org/wiki/Logical_disjunction -[invocation]: https://github.com/ucan-wg/invocation -[raw data multicodec]: https://github.com/multiformats/multicodec/blob/a03169371c0a4aec0083febc996c38c3846a0914/table.csv?plain=1#L41 -[secure hardware enclave]: https://support.apple.com/en-ca/guide/security/sec59b0b31ff -[spki rfc]: https://www.rfc-editor.org/rfc/rfc2693.html -[time definition]: https://en.wikipedia.org/wiki/Temporal_database -[ucan-uri]: https://github.com/ucan-wg/ucan-uri -[ucan.xyz]: https://ucan.xyz - - - - - - - - - - - - - -FIXME move to own spec? - -# 7. Collections - -FIXME update exmaple to latest format ...or break out into own spec :shrug: - -UCANs are indexed by their hash — often called their ["content address"][content addressable storage]. UCANs MUST be addressable as [CIDv1]. Use of a [canonical CID] is RECOMMENDED. - -Content addressing the proofs has multiple advantages over inlining tokens, including: -* Avoids re-encoding deeply nested proofs as Base64 many times (and the associated size increase) -* Canonical signature -* Enables only transmitting the relevant proofs - -Multiple UCANs in a single request MAY be collected into one table. It is RECOMMENDED that these be indexed by CID. The [canonical JSON representation][canonical collections] (below) MUST be supported. Implementations MAY include more formats, for example to optimize for a particular transport. Transports MAY map their collection to this collection format. - -### 7.1 Canonical JSON Collection - -The canonical JSON representation is an key-value object, mapping UCAN content identifiers to their fully-encoded base64url strings. A root "entry point" (if one exists) MUST be indexed by the slash `/` character. - -#### 7.1.1 Example - - -``` json -{ - "/": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono2TWtmUWhMSEJTRk11UjdiUVhUUWVxZTVrWVVXNTFIcGZaZWF5bWd5MXprUDJqTSIsImF0dCI6W3sid2l0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJsaWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUklURSJdfX0seyJ3aXRoIjp7InNjaGVtZSI6InduZnMiLCJoaWVyUGFydCI6Ii8vZGVtb3VzZXIuZmlzc2lvbi5uYW1lL3B1YmxpYy9ub3Rlcy8ifSwiY2FuIjp7Im5hbWVzcGFjZSI6InduZnMiLCJzZWdtZW50cyI6WyJPVkVSV1JJVEUiXX19XSwiZXhwIjo5MjU2OTM5NTA1LCJpc3MiOiJkaWQ6a2V5Ono2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsInByZiI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0luUjVjQ0k2SWtwWFZDSXNJblZqZGlJNklqQXVPQzR4SW4wLmV5SmhkV1FpT2lKa2FXUTZhMlY1T25vMlRXdHlOV0ZsWm1sdU1VUjZha2MzVFVKS00yNXpSa056Ym5aSVMwVjJWR0l5UXpSWlFVcDNZbmgwTVdwR1V5SXNJbUYwZENJNlczc2lkMmwwYUNJNmV5SnpZMmhsYldVaU9pSjNibVp6SWl3aWFHbGxjbEJoY25RaU9pSXZMMlJsYlc5MWMyVnlMbVpwYzNOcGIyNHVibUZ0WlM5d2RXSnNhV012Y0dodmRHOXpMeUo5TENKallXNGlPbnNpYm1GdFpYTndZV05sSWpvaWQyNW1jeUlzSW5ObFoyMWxiblJ6SWpwYklrOVdSVkpYVWtsVVJTSmRmWDFkTENKbGVIQWlPamt5TlRZNU16azFNRFVzSW1semN5STZJbVJwWkRwclpYazZlalpOYTJ0WGIzRTJVek4wY1ZKWGNXdFNibmxOWkZobWNuTTFORGxGWm5VMmNVTjFOSFZxUkdaTlkycEdVRXBTSWl3aWNISm1JanBiWFgwLlNqS2FIR18yQ2UwcGp1TkY1T0QtYjZqb04xU0lKTXBqS2pqbDRKRTYxX3VwT3J0dktvRFFTeFo3V2VZVkFJQVREbDhFbWNPS2o5T3FPU3cwVmc4VkNBIiwiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNklrcFhWQ0lzSW5WamRpSTZJakF1T0M0eEluMC5leUpoZFdRaU9pSmthV1E2YTJWNU9ubzJUV3R5TldGbFptbHVNVVI2YWtjM1RVSktNMjV6UmtOemJuWklTMFYyVkdJeVF6UlpRVXAzWW5oME1XcEdVeUlzSW1GMGRDSTZXM3NpZDJsMGFDSTZleUp6WTJobGJXVWlPaUozYm1aeklpd2lhR2xsY2xCaGNuUWlPaUl2TDJSbGJXOTFjMlZ5TG1acGMzTnBiMjR1Ym1GdFpTOXdkV0pzYVdNdmNHaHZkRzl6THlKOUxDSmpZVzRpT25zaWJtRnRaWE53WVdObElqb2lkMjVtY3lJc0luTmxaMjFsYm5SeklqcGJJazlXUlZKWFVrbFVSU0pkZlgxZExDSmxlSEFpT2preU5UWTVNemsxTURVc0ltbHpjeUk2SW1ScFpEcHJaWGs2ZWpaTmEydFhiM0UyVXpOMGNWSlhjV3RTYm5sTlpGaG1jbk0xTkRsRlpuVTJjVU4xTkhWcVJHWk5ZMnBHVUVwU0lpd2ljSEptSWpwYlhYMC5TakthSEdfMkNlMHBqdU5GNU9ELWI2am9OMVNJSk1waktqamw0SkU2MV91cE9ydHZLb0RRU3haN1dlWVZBSUFURGw4RW1jT0tqOU9xT1N3MFZnOFZDQSJdfQ.Ab-xfYRoqYEHuo-252MKXDSiOZkLD-h1gHt8gKBP0AVdJZ6Jruv49TLZOvgWy9QkCpiwKUeGVbHodKcVx-azCQ", - "bafkreihogico5an3e2xy3fykalfwxxry7itbhfcgq6f47sif6d7w6uk2ze": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVhdiI6IjAuMS4wIn0.eyJhdWQiOiJkaWQ6a2V5OnpTdEVacHpTTXRUdDlrMnZzemd2Q3dGNGZMUVFTeUExNVc1QVE0ejNBUjZCeDRlRko1Y3JKRmJ1R3hLbWJtYTQiLCJpc3MiOiJkaWQ6a2V5Ono1QzRmdVAyRERKQ2hoTUJDd0FrcFlVTXVKWmROV1dINU5lWWpVeVk4YnRZZnpEaDNhSHdUNXBpY0hyOVR0anEiLCJuYmYiOjE1ODg3MTM2MjIsImV4cCI6MTU4OTAwMDAwMCwic2NwIjoiLyIsInB0YyI6IkFQUEVORCIsInByZiI6bnVsbH0.Ay8C5ajYWHxtD8y0msla5IJ8VFffTHgVq448Hlr818JtNaTUzNIwFiuutEMECGTy69hV9Xu9bxGxTe0TpC7AzV34p0wSFax075mC3w9JYB8yqck_MEBg_dZ1xlJCfDve60AHseKPtbr2emp6hZVfTpQGZzusstimAxyYPrQUWv9wqTFmin0Ls-loAWamleUZoE1Tarlp_0h9SeV614RfRTC0e3x_VP9Ra_84JhJHZ7kiLf44TnyPl_9AbzuMdDwCvu-zXjd_jMlDyYcuwamJ15XqrgykLOm0WTREgr_sNLVciXBXd6EQ-Zh2L7hd38noJm1P_MIr9_EDRWAhoRLXPQ" -} -``` - From cc9bbc5ada0c46f997cf87fc0a0c880f6364fb2c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 11 Oct 2023 21:25:05 -0700 Subject: [PATCH 021/134] Resource & Ability --- README.md | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c8d07a4..29edb6f 100644 --- a/README.md +++ b/README.md @@ -228,23 +228,25 @@ For example: { // ... cap: { - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*" - //└───────────────────────────┬────────────────────────────┘ - // SUBJECT + "did:web:example.com": "ucan/*" + //└─────────┬─────────┘ + // Subject } } ``` #### 3.2.6.2 Resource +Unlike Subjects and Abilities, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. + By default, the Resource of a capability is the Subject. This makes the delegation chain self-certifying. ``` js { // ... "cap": { - // Subject & Resource - //┌────────────────────────────┴────────────────────────────┐ + // Subject & Resource + //┌───────────────────────────┴────────────────────────────┐ "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { "crud/update": { "status": "draft" @@ -254,18 +256,14 @@ By default, the Resource of a capability is the Subject. This makes the delegati } ``` -In the case where access to an external resource is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Caveat](s), except where it would be clearer to do otherwise. - - -FIXME point at /spec#5.5 external resources - +In the case where access to an [external resource] is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Caveat](s), except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor. ``` js { // ... "cap": { - // Subject - //┌────────────────────────────┴────────────────────────────┐ + // Subject + //┌───────────────────────────┴────────────────────────────┐ "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { "crud/create": { "uri": "https://blog.example.com/blog/", @@ -289,12 +287,28 @@ FIXME point at /spec#5.5 external resources Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. -Abilities MUST be defined by the Subject. While it is correct in this narrow context to think about the Subject as a namespace, +``` js +{ + // ... + "cap": { + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + // Ability + // ┌─────┴─────┐ + "crud/create": { + "uri": "https://blog.example.com/blog/", + "status": "draft" + } + } + } +} +``` + +Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. 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. #### 3.2.6.4 Caveats Caveat semantics MUST be defined by the Subject. - + * [ ] FIXME add _so much_ clarification - Semantics defined by resource & OG issuer - push resource into caveats @@ -634,3 +648,4 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[external resource]: FIXME /spec sectioj 5.5 diagram From f6eb8e52e0e23d97565dc0db44df27ed57af0174 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 11 Oct 2023 21:54:38 -0700 Subject: [PATCH 022/134] move sections around --- README.md | 153 +++++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 29edb6f..850b399 100644 --- a/README.md +++ b/README.md @@ -199,9 +199,13 @@ The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowled } ``` -### 3.2.6 Capabilities & Attenuation +# 4. Capabilities -Capabilities MUST be presented as a map under the `cap` field. This map is REQUIRED but MAY be empty. +Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: + +``` +{ $SUBJECT: { $ABILITY: $CAVEATS } } +``` This map MUST contain some or none of the following: 1. A strict subset (attenuation) of the capability authority from the next direct `prf` field @@ -210,15 +214,28 @@ This map MUST contain some or none of the following: The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. -The capabilities take this form: +Here is an illustrative example: +``` js +{ + // ... + "cap": { + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "crud/create": { + "uri": "https://blog.example.com/blog/", + "status": "draft" + }, + "msg/send": { + "sender": "mailto:alice@example.com", + "subject": "Weekly Reports", + "day": "friday" + } + } + } +} ``` -{ $SUBJECT: { $ABILITY: $CAVEATS } } -``` - -FIXME example -#### 3.2.6.1 Subject +## 4.1 Subject The Subject MUST be the DID that initiated the delegation chain. @@ -235,7 +252,7 @@ For example: } ``` -#### 3.2.6.2 Resource +## 4.2 Resource Unlike Subjects and Abilities, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. @@ -283,7 +300,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS } ``` -#### 3.2.6.3 Abilities +## 4.3 Abilities Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. @@ -303,25 +320,58 @@ Abilities MUST be presented as a string. By convention, abilities SHOULD be name } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. 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. +Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. 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. [`*` gives full access] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. -#### 3.2.6.4 Caveats +### 4.3.1 Reserved Abilities -Caveat semantics MUST be defined by the Subject. - * [ ] -FIXME add _so much_ clarification -- Semantics defined by resource & OG issuer -- push resource into caveats -- optional resource, message passing analogy -- Normal form and compact form +#### 43.1.1 `ucan` +The `ucan` abilty namespace MUST be reserved. Support for `ucan/*` is RECOMMENDED +FIXME -Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST prevent invocation otherwise. Caveats MUST be formatted as objects. +#### 4.3.1.2 "Top" Ability -On validation, the caveat array MUST be treated as a logically disjunct (an "OR", NOT an "and"). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: +The "top" (or "super user") ability MUST be denoted `*`. The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. Top corresponds to an "all" matcher, whereas [delegation] corresponds to "any" in the UCAN chain. The top ability is useful when "linking" agents by delegating all access to resource(s). This is the most powerful ability, and as such it SHOULD be handled with care. + +``` mermaid +%%{ init: { 'flowchart': { 'curve': 'linear' } } }%% + +flowchart BT + * + + msg/* --> * + subgraph msgGraph [ ] + msg/send --> msg/* + msg/receive --> msg/* + end + + crud/* --> * + subgraph crudGraph [ ] + crud/read --> crud/* + crud/mutate --> crud/* + + subgraph mutationGraph [ ] + crud/create --> crud/mutate + crud/update --> crud/mutate + crud/destroy --> crud/mutate + end + end + + ... --> * +``` + +#### 4.3.1.3 Bottom + +In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. +## 4.4 Caveats +Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensivle, but vocabularies may be reused across many Subjects. + +Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST be formatted as maps. + +On validation, the caveat array MUST be treated as a logically disjunct (an "OR", NOT an "and"). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: ``` json { @@ -357,6 +407,10 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. +### 4.4.1 Normal Form + + + | Subject | Ability | Caveat | |------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| | `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | @@ -366,8 +420,6 @@ The above MUST be interpreted as the set of capabilities below. If _any_ are mat | `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | -### XXXX.XXXXX Normal Form - Note that all caveats need to be understable to th eexecitor All of the above can be validly expressed in [DNF]. @@ -408,11 +460,13 @@ All of the above can be validly expressed in [DNF]. } ``` +### 4.4.2 Compact Form +### 4.4.3 Top Caveat @@ -438,59 +492,16 @@ Note that for consistency in this syntax, the empty array MUST be equivalent to | `[]` | No capabilities | | `{}`, `[{}]`, `[[{}]]` | Full capabilities for this resource/ability pair (no caveats) | + * - subgraph msgGraph [ ] - msg/send --> msg/* - msg/receive --> msg/* - end - - crud/* --> * - subgraph crudGraph [ ] - crud/read --> crud/* - crud/mutate --> crud/* - - subgraph mutationGraph [ ] - crud/create --> crud/mutate - crud/update --> crud/mutate - crud/destroy --> crud/mutate - end - end - - ... --> * -``` - -### 4.2.1 Bottom - -In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. + + --> # 5 Validation From 007c3456e60241846b08fea1108d50b8fcff3cd3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 09:52:29 -0700 Subject: [PATCH 023/134] DNF diagram --- README.md | 106 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 850b399..b87af73 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ # UCAN Delegation Specification v1.0.0-rc.1 - - -TODOs -- externally owned resources - - ## Editors * [Brooklyn Zelenka], [Fission] @@ -51,10 +45,10 @@ The overall container of a header, claims, and signature remain as per [RFC 7519 The header is a standard JWT header, and MUST include all of the following fields: -| Field | Type | Description | Required | -|-------|----------|--------------------------------|----------| -| `alg` | `String` | Signature algorithm | Yes | -| `typ` | `String` | Type (MUST be `"JWT"`) | Yes | +| Field | Type | Description | Required | +|-------|----------|------------------------|----------| +| `alg` | `String` | Signature algorithm | Yes | +| `typ` | `String` | Type (MUST be `"JWT"`) | Yes | ### 3.1.1 Algorithms @@ -409,6 +403,33 @@ The above MUST be interpreted as the set of capabilities below. If _any_ are mat ### 4.4.1 Normal Form +Caveats MAY be expressed in a compact form, but any caveat MUST be expressable in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. + +Normal form MUST take the following shape: `[[{}]]`. The outer array represents a logical `any` (chained `OR`s), and the inner arrays represent logical `all` (chained `AND`s). + +For instance, the following represents `({a: 1, b:2} AND {c: 3}) OR {d: 4}`: + +``` js +[ + [ + { // ┐ + a: 1, // ├─ Caveat ┐ + b: 2 // │ │ + }, // ┘ ├─ AND ┐ + { // ┐ │ │ + c: 3 // ├─ Caveat ┘ │ + } // ┘ ├─ OR + ], // │ + [ // │ + {d: 4} // ]─ Caveat ───────┘ + ] +] +``` + + + + + | Subject | Ability | Caveat | @@ -472,38 +493,9 @@ All of the above can be validly expressed in [DNF]. -The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. - -| Proof Caveats | Delegated Caveats | Is Valid? | Comment | -|---------------|-------------------|-----------|----------------------------------------| -| `[{}]` | `[{}]` | Yes | Equal | -| `[x]` | `[x]` | Yes | Equal | -| `[x]` | `[{}]` | No | Escalation to any | -| `[{}]` | `[x]` | Yes | Attenuates the `{}` caveat to `x` | -| `[x]` | `[y]` | No | Escalation by using a different caveat | -| `[x, y]` | `[x]` | Yes | Removes a capability | -| `[x, y]` | `[x, (y + z)]` | Yes | Attenuates existing caveat | -| `[x, y]` | `[x, y, z]` | No | Escalation by adding new capability | - -Note that for consistency in this syntax, the empty array MUST be equivalent to disallowing the capability. Conversely, an empty object MUST be treated as "no caveats". - -| Proof Caveats | Comment | -|------------------------|---------------------------------------------------------------| -| `[]` | No capabilities | -| `{}`, `[{}]`, `[[{}]]` | Full capabilities for this resource/ability pair (no caveats) | - - -# 5 Validation FIXME note that validation happens at exection time @@ -608,9 +600,41 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. +## 5.4 Caveat Attenuation + +The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. + +| Proof Caveats | Delegated Caveats | Is Valid? | Comment | +|---------------|-------------------|-----------|----------------------------------------| +| `[{}]` | `[{}]` | Yes | Equal | +| `[x]` | `[x]` | Yes | Equal | +| `[x]` | `[{}]` | No | Escalation to any | +| `[{}]` | `[x]` | Yes | Attenuates the `{}` caveat to `x` | +| `[x]` | `[y]` | No | Escalation by using a different caveat | +| `[x, y]` | `[x]` | Yes | Removes a capability | +| `[x, y]` | `[x, (y + z)]` | Yes | Attenuates existing caveat | +| `[x, y]` | `[x, y, z]` | No | Escalation by adding new capability | + +Note that for consistency in this syntax, the empty array MUST be equivalent to disallowing the capability. Conversely, an empty object MUST be treated as "no caveats". + +| Proof Caveats | Comment | +|------------------------|---------------------------------------------------------------| +| `[]` | No capabilities | +| `{}`, `[{}]`, `[[{}]]` | Full capabilities for this resource/ability pair (no caveats) | + + -## 5.5 Content Identifiers +# 6. Content Identifiers A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. From 6a1f23dba537b12799d2861cc13ab4d04df6d7cf Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 10:09:20 -0700 Subject: [PATCH 024/134] More DNF examples --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b87af73..c4cfd20 100644 --- a/README.md +++ b/README.md @@ -413,19 +413,68 @@ For instance, the following represents `({a: 1, b:2} AND {c: 3}) OR {d: 4}`: [ [ { // ┐ - a: 1, // ├─ Caveat ┐ - b: 2 // │ │ - }, // ┘ ├─ AND ┐ - { // ┐ │ │ - c: 3 // ├─ Caveat ┘ │ - } // ┘ ├─ OR - ], // │ - [ // │ - {d: 4} // ]─ Caveat ───────┘ + a: 1, // ├─ Caveat ─┐ + b: 2 // │ │ + }, // ┘ ├─ AND ┐ + { // ┐ │ │ + c: 3 // ├─ Caveat ─┘ │ + } // ┘ ├─ OR + ], // │ + [ // │ + { // ┐ │ + d: 4 // ├─ Caveat ────────┘ + } // ┘ ] ] ``` +Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, if a root UCAN caveat has a `tag` field but no `tags` field, it is still possible to express multiplicity by adding another `AND`ed caveat: + +``` js +// Original Caveat +[ + [ + { + "uri": "https://blog.example.com/", + "tag": "news" + } + ] +] + +// Attenuated Caveat +[ + [ + { + "uri": "https://blog.example.com/", + "tag": "news" + }, + // AND + { + "tag": "breaking" + } + ] +] +``` + +In this example, the original caveat had not accounted for there being multiple tags at runtime. The attenuated capability grants access to blog posts at `https://blog.example.com/"` that are tagged with both `"news"` and `"breaking"` due to the `AND` in the predicate. + + +This is also helpful if each object has a special meaning or sense: + +``` js +[ + [ + { + "type": "path", + "segments": ["blog", "october"] + }, + { + "type": "market", + "segments": ["manufacturing", "healthcare", "service", "technology"] + } + ] +] +``` From 878a3459997d35dbd6769621ba0ec42ca0221d52 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 10:28:01 -0700 Subject: [PATCH 025/134] Start on compact form --- README.md | 115 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c4cfd20..7b080de 100644 --- a/README.md +++ b/README.md @@ -458,7 +458,6 @@ Expressing caveats in this standard way simplifies ad hoc extension at delegatio In this example, the original caveat had not accounted for there being multiple tags at runtime. The attenuated capability grants access to blog posts at `https://blog.example.com/"` that are tagged with both `"news"` and `"breaking"` due to the `AND` in the predicate. - This is also helpful if each object has a special meaning or sense: ``` js @@ -476,53 +475,64 @@ This is also helpful if each object has a special meaning or sense: ] ``` +Note that while adding whole objects is useful in many situation as above, attenuation MAY also be achieved by adding fields to an object: +``` js +// Original Caveat +[ + [ + { + "uri": "https://blog.example.com/", + "tag": "news" + } + ] +] +// Attenuated Caveat +[ + [ + { + "uri": "https://blog.example.com/", + "tag": "news", + "status": "draft" // New field + } + ] +] +``` +FIXME empty case +### 4.4.2 Compact Form -| Subject | Ability | Caveat | -|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| -| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | -| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | -| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | -| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | -| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | - - -Note that all caveats need to be understable to th eexecitor - -All of the above can be validly expressed in [DNF]. +Normal from is consistent, but needlessly verbose for simple cases. Compact form omits superflous branches. Consider the following normal form capabilities: ``` json { "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { "ucan/*": [[{}]] }, - "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": { - "crud/create": [ FIXME: ambiguity if you only pass crud/create, then later they add a resource. You want to know *what* you can update - [ - { "uri": "dns:example.com" }, - { "record": "TXT" } - ] - ] - }, "did:web:example.com": { "crud/read": [ [ - { "uri": "https://blog.example.com" }, - { "status": "published" } + { + "uri": "https://blog.example.com", + "status": "published" + } ] ], "crud/update": [ [ - { "uri": "https://example.com/newsletter/" }, - { "status": "draft" } + { + "uri": "https://example.com/newsletter/", + "status": "draft" + } ], [ - { "uri": "https://blog.example.com" }, - { "status": "published" }, - { "tag": "news" }, + { + "uri": "https://blog.example.com", + "status": "published", + "tag": "news" + }, { "tag": "breaking" } ] ] @@ -530,7 +540,54 @@ All of the above can be validly expressed in [DNF]. } ``` -### 4.4.2 Compact Form +The above MAY be expressed in compact form as follows: + +``` json +{ + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", + "did:web:example.com": { + "crud/read": { + "uri": "https://blog.example.com", + "status": "published" + }, + "crud/update": [ + { + "uri": "https://example.com/newsletter/", + "status": "draft" + }, + [ + { + "uri": "https://blog.example.com", + "status": "published", + "tag": "news" + }, + { "tag": "breaking" } + ] + ] + } +} +``` + + + + +FIXME attenuation can add fields + + + + + +| Subject | Ability | Caveat | +|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| +| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | +| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | +| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | +| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | +| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | + + +Note that all caveats need to be understable to th eexecitor + From 4e9e6e0aad734449083e90935d0af1c99d47093a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 11:11:43 -0700 Subject: [PATCH 026/134] Wrapping up caveats --- README.md | 104 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 7b080de..102071f 100644 --- a/README.md +++ b/README.md @@ -399,9 +399,58 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" } ``` -The above MUST be interpreted as the set of capabilities below. If _any_ are matched, the check MUST pass validation. +The above MUST be interpreted as the set of capabilities below in the following table: -### 4.4.1 Normal Form +| Subject | Ability | Caveat | +|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| +| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | +| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | +| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | +| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | +| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | + +When validating a delegation chain in the abstract, all caveats MUST be present in each sucessive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. +Note that all caveats need to be understable to th execitor + +### 4.4.1 All or Nothing + +Caveats MAY include user-supplied fields, but there are two default cases that MUST be supported. These are called the "top" and "bottom" caveats. + +#### 4.4.1.1 The Top Caveat + +The top caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[[{}]]`. In compact form, the caveat array MAY be omitted. + +``` js +// Normal Form +{ + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "some/ability": [[{}]] + } +} + +// Compact Form +{ + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "some/ability", +} +``` + +#### 4.4.1.2 The Bottom Caveat + +The explicitly empty caveat (`[[]]`) MUST represent disallowing any action on the resource. In predicate logic terms, this represents `false`. While possible to express with the bottom caveat, it is equivalent to simply omitting the affected branches or capabilities. For reasons of legibility and size, omission is RECOMMENDED. + +``` js +{ + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "some/ability": [[]] // Equivalent to omitting this ability + } +} +``` + +### 4.4.2 Serialization + +Caveats have two isomorphic serilaizations: [compact form] and [normal form]. It is often easiest for validators to expand to normal form prior to checking. Compact form is more easily legible and requires fewer bytes. + +### 4.4.2.1 Normal Form Caveats MAY be expressed in a compact form, but any caveat MUST be expressable in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. @@ -415,14 +464,14 @@ For instance, the following represents `({a: 1, b:2} AND {c: 3}) OR {d: 4}`: { // ┐ a: 1, // ├─ Caveat ─┐ b: 2 // │ │ - }, // ┘ ├─ AND ┐ - { // ┐ │ │ - c: 3 // ├─ Caveat ─┘ │ - } // ┘ ├─ OR - ], // │ - [ // │ - { // ┐ │ - d: 4 // ├─ Caveat ────────┘ + }, // ┘ ├─ AND ─┐ + { // ┐ │ │ + c: 3 // ├─ Caveat ─┘ │ + } // ┘ ├─ OR + ], // │ + [ // │ + { // ┐ │ + d: 4 // ├─ Caveat ─────────┘ } // ┘ ] ] @@ -500,9 +549,7 @@ Note that while adding whole objects is useful in many situation as above, atten ] ``` -FIXME empty case - -### 4.4.2 Compact Form +### 4.4.2.1 Compact Form Normal from is consistent, but needlessly verbose for simple cases. Compact form omits superflous branches. Consider the following normal form capabilities: @@ -568,37 +615,6 @@ The above MAY be expressed in compact form as follows: } ``` - - - -FIXME attenuation can add fields - - - - - -| Subject | Ability | Caveat | -|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| -| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | -| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | -| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | -| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | -| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | - - -Note that all caveats need to be understable to th eexecitor - - - - - - -### 4.4.3 Top Caveat - - - - - # 5 Validation From 2cb8c4ac9a0fef6a7905b8d307cb143c1ee45e43 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 11:30:43 -0700 Subject: [PATCH 027/134] Bones in place, now to clean up --- README.md | 58 +++++++++++++++++++------------------------------------ 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 102071f..bca867f 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,7 @@ Caveats define a set of constraints on what can be redelegated or invoked. Cavea Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST be formatted as maps. -On validation, the caveat array MUST be treated as a logically disjunct (an "OR", NOT an "and"). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: +On validation, the caveat array MUST be treated as a logically disjunct (`OR`). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: ``` json { @@ -392,7 +392,7 @@ On validation, the caveat array MUST be treated as a logically disjunct (an "OR" "status": "published" "tag": "news", } - { "tag": "breaking" }, + { "tag": "breaking" } ] ] } @@ -617,11 +617,9 @@ The above MAY be expressed in compact form as follows: # 5 Validation +Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. - -FIXME note that validation happens at exection time - -Each capability has its own semantics, which needs to be interpretable by the target resource handler. Therefore, a validator MUST NOT reject all capabilities when only one is not understood. +Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a caveat fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: @@ -726,35 +724,20 @@ A good litmus test for invocation validity by a invoking agent is to check if th The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. -| Proof Caveats | Delegated Caveats | Is Valid? | Comment | -|---------------|-------------------|-----------|----------------------------------------| -| `[{}]` | `[{}]` | Yes | Equal | -| `[x]` | `[x]` | Yes | Equal | -| `[x]` | `[{}]` | No | Escalation to any | -| `[{}]` | `[x]` | Yes | Attenuates the `{}` caveat to `x` | -| `[x]` | `[y]` | No | Escalation by using a different caveat | -| `[x, y]` | `[x]` | Yes | Removes a capability | -| `[x, y]` | `[x, (y + z)]` | Yes | Attenuates existing caveat | -| `[x, y]` | `[x, y, z]` | No | Escalation by adding new capability | - -Note that for consistency in this syntax, the empty array MUST be equivalent to disallowing the capability. Conversely, an empty object MUST be treated as "no caveats". - -| Proof Caveats | Comment | -|------------------------|---------------------------------------------------------------| -| `[]` | No capabilities | -| `{}`, `[{}]`, `[[{}]]` | Full capabilities for this resource/ability pair (no caveats) | - - - +Here are some abstract cases given in [normal form]. + +| Proof Caveats | Delegated Caveats | Is Valid? | Comment | +|------------------------|------------------------------------|-----------|------------------------------------------------| +| `[[{}]]` | `[[{}]]` | Yes | Equal | +| `[[{a: 1}]]` | `[[{a: 1}]]` | Yes | Equal | +| `[[{a: 1}]]` | `[[{}]]` | No | Escalation by removing fields | +| `[[{}]]` | `[[{a: 1}]]` | Yes | Attenuates `{}` by adding fields | +| `[[{a: 1}]]` | `[[{b: 2}]]` | No | Escalation by using a different caveat | +| `[{a: 1}], [{b: 2}]]` | `[[{a: 1}]]` | Yes | Removes a capability (removes an `OR` branch) | +| `[[{a: 1}], [{b: 2}]]` | `[[{a: 1}], [{b: 2, c: 3}]]` | Yes | Attenuates existing caveat | +| `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]]` | No | Escalation by adding new capability | +| `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]` | No | Escalation by adding new capability (`{b: 2}`) | +| `[[{a: 1}]]` | `[[{a: 1, b: 2}], [{a: 1, c: 3}]]` | Yes | Attenuates the original capability | # 6. Content Identifiers @@ -764,7 +747,7 @@ The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other cod The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. Please refer to [token resolution] for more. -## 5.5.1 CID Canonicalization +## 6.1 CID Canonicalization A canonical CID can be important for some use cases, such as caching and [revocation]. A canonical CID MUST conform to the following: @@ -773,8 +756,7 @@ A canonical CID can be important for some use cases, such as caching and [revoca * [SHA2-256] * [Raw data multicodec] (`0x55`) - -# 9. Acknowledgments +# 7. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. From e1878d7fb7e1110e9bafd039c0cc37fa94d11231 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 11:49:08 -0700 Subject: [PATCH 028/134] Adding links --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bca867f..b960e00 100644 --- a/README.md +++ b/README.md @@ -787,4 +787,28 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[external resource]: FIXME /spec sectioj 5.5 diagram +[Alan Karp]: https://github.com/alanhkarp +[Benjamin Goering]: https://github.com/gobengo +[Blaine Cook]: https://github.com/blaine +[Bluesky]: https://blueskyweb.xyz/ +[Brendan O'Brien]: https://github.com/b5 +[Brian Ginsburg]: https://github.com/bgins +[Brooklyn Zelenka]: https://github.com/expede +[Christine Lemmer-Webber]: https://github.com/cwebber +[Christopher Joel]: https://github.com/cdata +[Dan Finlay]: https://github.com/danfinlay +[Daniel Holmgren]: https://github.com/dholms +[Fission]: https://fission.codes +[Hugo Dias]: https://github.com/hugomrdias +[Ink & Switch]: https://www.inkandswitch.com/ +[Irakli Gozalishvili]: https://github.com/Gozala +[JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +[Juan Caballero]: https://github.com/bumblefudge +[Mark Miller]: https://github.com/erights +[Martin Kleppmann]: https://martin.kleppmann.com/ +[Mikael Rogers]: https://github.com/mikeal/ +[Philipp Krüger]: https://github.com/matheus23 +[Protocol Labs]: https://protocol.ai/ +[SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ +[SPKI]: https://theworld.com/~cme/html/spki.html +[external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems From dbefb338b0f1a4d0aad5395ab5cb3df3b6605a73 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 12:06:25 -0700 Subject: [PATCH 029/134] Add many missing links --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b960e00..bb9a5ca 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ The header is a standard JWT header, and MUST include all of the following field It is RECOMMENDED that the following algorithms be supported: -- [RS256] +- [ES256] - [EdDSA][RFC 8037] -- [P-256 ECDSA] +- [RS256] All algorithms MUST match the DID principal in the `iss` field. This enforces that the `alg` field MUST be asymmetric (public key cryptography or nonstandard but emerging patterns like smart contract signatures) @@ -794,21 +794,34 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Brendan O'Brien]: https://github.com/b5 [Brian Ginsburg]: https://github.com/bgins [Brooklyn Zelenka]: https://github.com/expede +[CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata +[DID]: https://www.w3.org/TR/did-core/ [Dan Finlay]: https://github.com/danfinlay [Daniel Holmgren]: https://github.com/dholms +[ES256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.4 +[Executor]: https://github.com/ucan-wg/spec#31-roles [Fission]: https://fission.codes [Hugo Dias]: https://github.com/hugomrdias [Ink & Switch]: https://www.inkandswitch.com/ [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number +[JWT]: https://www.rfc-editor.org/rfc/rfc7519 [Juan Caballero]: https://github.com/bumblefudge [Mark Miller]: https://github.com/erights [Martin Kleppmann]: https://martin.kleppmann.com/ [Mikael Rogers]: https://github.com/mikeal/ [Philipp Krüger]: https://github.com/matheus23 [Protocol Labs]: https://protocol.ai/ +[RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 +[RS256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.3 +[SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html +[UCAN]: https://github.com/ucan-wg/spec +[base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 +[did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 +[did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 +[did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems From db7b6872d4ce5e1e1429c9171b68bd9ffc50e3b2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 12:58:42 -0700 Subject: [PATCH 030/134] Avoid RS256 and P256 --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bb9a5ca..c9f471c 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ The header is a standard JWT header, and MUST include all of the following field ### 3.1.1 Algorithms -It is RECOMMENDED that the following algorithms be supported: +The following algorithms are RECOMMENDED to be validatable: - [ES256] - [EdDSA][RFC 8037] @@ -100,11 +100,7 @@ The `iss` and `aud` fields MUST contain a single principal each. If an issuer's DID has more than one key (e.g. [`did:ion`], [`did:3`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. -It is RECOMMENDED that the following `did:key` types be supported: - -- [RSA][did:key RSA] -- [EdDSA][did:key EdDSA] -- [P-256 ECDSA][did:key ECDSA] +[EdDSA] `did:key`s MUST be suppoted, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. Note that every [Subject] MUST correspond to a root delegation issuer. @@ -505,7 +501,7 @@ Expressing caveats in this standard way simplifies ad hoc extension at delegatio ] ``` -In this example, the original caveat had not accounted for there being multiple tags at runtime. The attenuated capability grants access to blog posts at `https://blog.example.com/"` that are tagged with both `"news"` and `"breaking"` due to the `AND` in the predicate. +In this example, the original caveat had not accounted for there being multiple tags at runtime. The attenuated capability grants access to blog posts at `https://blog.example.com/` that are tagged with both `news` and `breaking` due to the `AND` in the predicate. This is also helpful if each object has a special meaning or sense: @@ -784,6 +780,8 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[Facts] +[* gives full access] @@ -825,3 +823,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems + +[DNF] +[POLA] + From f492ade6f94eb49f778163867bcaa1050affff34 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 15:37:12 -0700 Subject: [PATCH 031/134] More box diagramming --- README.md | 61 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c9f471c..1fdc246 100644 --- a/README.md +++ b/README.md @@ -314,15 +314,28 @@ Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. ### 4.3.1 Reserved Abilities -#### 43.1.1 `ucan` +#### 43.1.1 `ucan` Namespace -The `ucan` abilty namespace MUST be reserved. Support for `ucan/*` is RECOMMENDED +The `ucan` abilty namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` -FIXME +Support for the `ucan/*` delegated proof ability is RECOMMENDED. -#### 4.3.1.2 "Top" Ability +#### 4.3.1.2 `*` AKA "Top" -The "top" (or "super user") ability MUST be denoted `*`. The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. Top corresponds to an "all" matcher, whereas [delegation] corresponds to "any" in the UCAN chain. The top ability is useful when "linking" agents by delegating all access to resource(s). This is the most powerful ability, and as such it SHOULD be handled with care. +_"Top" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ + +The "top" (or "any") ability MUST be denoted `*`. This can be thought of as something akin to a super user permission in RBAC. + +``` js +{ + // ... Top + "cap": { //┌┴┐ + "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "*" + } +} +``` + +The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. The top ability is useful when "linking" agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delgate to update a Subject's mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others. ``` mermaid %%{ init: { 'flowchart': { 'curve': 'linear' } } }%% @@ -363,32 +376,34 @@ Caveats MAY be open ended. Caveats MUST be understood by the executor of the eve On validation, the caveat array MUST be treated as a logically disjunct (`OR`). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: -``` json +``` js { "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": { - "crud/create": { - "uri": "dns:example.com", - "record": "TXT" - } + "crud/create": { + "uri": "dns:example.com", // ┐ + "record": "TXT" // ├─ Caveat + } // ┘ }, "did:web:example.com": { "crud/read": { - "uri": "https://blog.example.com", - "status": "published" - }, + "uri": "https://blog.example.com", // ┐ + "status": "published" // ├─ Caveat + }, // ┘ "crud/update": [ - { - "uri": "https://example.com/newsletter/", - "status": "draft" - }, + { // ┐ + "uri": "https://example.com/newsletter/", // ├─ Caveat + "status": "draft" // │ + }, // ┘ [ - { - "uri": "https://blog.example.com", - "status": "published" - "tag": "news", - } - { "tag": "breaking" } + { // ┐ + "uri": "https://blog.example.com", // │ + "status": "published" // ├─ Caveat + "tag": "news", // │ + }, // ┘ + { // ┐ + "tag": "breaking" // ├ Caveat + } // ┘ ] ] } From 3954f5d63482fbaa04d8f3af04df7975beef9f09 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 15:48:22 -0700 Subject: [PATCH 032/134] did:3 -> did:plc due to deprecation --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1fdc246..56f69a1 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,8 @@ The `iss` and `aud` fields describe the token's principals. They are distinguish The `iss` and `aud` fields MUST contain a single principal each. -If an issuer's DID has more than one key (e.g. [`did:ion`], [`did:3`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. - +If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:ion`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. + [EdDSA] `did:key`s MUST be suppoted, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. Note that every [Subject] MUST correspond to a root delegation issuer. @@ -117,13 +117,13 @@ Note that every [Subject] MUST correspond to a root delegation issuer. ``` ```json -"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", +"aud": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "iss": "did:ion:test:EiANCLg1uCmxUR4IUkpW8Y5_nuuXLbAEwonQd4q8pflTnw#key-1", ``` ```json -"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", -"iss": "did:3:bafyreiffkeeq4wq2htejqla2is5ognligi4lvjhwrpqpl2kazjdoecmugi#yh27jTt7Ny2Pwdy", +"aud": "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", +"iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", ``` ### 3.2.3 Time Bounds @@ -132,7 +132,7 @@ Note that every [Subject] MUST correspond to a root delegation issuer. The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. -The `exp` field MUST be set. Following the [principle of least authority][POLA], it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid. +The `exp` field MUST be set. Following the [principle of least authority][PoLA], it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid. Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. @@ -292,7 +292,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS ## 4.3 Abilities -Abilities MUST be presented as a string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. +Abilities MUST be presented as a case-insensitive string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. ``` js { @@ -310,7 +310,7 @@ Abilities MUST be presented as a string. By convention, abilities SHOULD be name } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. 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. [`*` gives full access] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. ### 4.3.1 Reserved Abilities @@ -402,7 +402,7 @@ On validation, the caveat array MUST be treated as a logically disjunct (`OR`). "tag": "news", // │ }, // ┘ { // ┐ - "tag": "breaking" // ├ Caveat + "tag": "breaking" // ├─ Caveat } // ┘ ] ] @@ -811,9 +811,11 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata [DID]: https://www.w3.org/TR/did-core/ +[DNF]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [Dan Finlay]: https://github.com/danfinlay [Daniel Holmgren]: https://github.com/dholms [ES256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.4 +[EdDSA]: https://en.wikipedia.org/wiki/EdDSA [Executor]: https://github.com/ucan-wg/spec#31-roles [Fission]: https://fission.codes [Hugo Dias]: https://github.com/hugomrdias @@ -826,6 +828,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Martin Kleppmann]: https://martin.kleppmann.com/ [Mikael Rogers]: https://github.com/mikeal/ [Philipp Krüger]: https://github.com/matheus23 +[PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [Protocol Labs]: https://protocol.ai/ [RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 [RS256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.3 @@ -833,12 +836,12 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html [UCAN]: https://github.com/ucan-wg/spec +[ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ +[`did:ion`]: https://identity.foundation/ion/ +[`did:key`]: https://w3c-ccg.github.io/did-method-key/ +[`did:plc`]: https://github.com/did-method-plc/did-method-plc [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 [did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems - -[DNF] -[POLA] - From ac2abf4a641e71999f4f149780921ea4794ead70 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:00:10 -0700 Subject: [PATCH 033/134] Clean up example headers, remove minor fixems --- README.md | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 56f69a1..bb6ff01 100644 --- a/README.md +++ b/README.md @@ -64,12 +64,12 @@ The JWT `"alg": "none"` MUST NOT be supported, as the lack of signature prevents Symmetric algorithms such as HMACs (e.g. `"alg": "HS256"`) MUST NOT be supported, since they cannot be used to prove control over the issuer DID. -### 3.1.1 Examples +Here is a common example: ```json { "alg": "EdDSA", - "typ": "JWT", + "typ": "JWT" } ``` @@ -104,7 +104,7 @@ If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:ion`]), Note that every [Subject] MUST correspond to a root delegation issuer. -#### 3.2.2.1 Examples +Below are a couple examples: ```json "aud": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", @@ -140,13 +140,28 @@ Several named points of time in the UCAN lifecycle [can be found in the high lev [^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. -#### 3.2.3.1 Example +Below are a couple examples: ```js { + // ... "nbf": 1529496683, - "exp": 1575606941, + "exp": 1575606941 +} +``` + +```js +{ // ... + "exp": 1575606941 +} +``` + +```js +{ + // ... + "nbf": 1529496683, + "exp": null } ``` @@ -158,25 +173,24 @@ The recommeneded size of the nonce differs by key type. In many cases, a random This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`fct`][Facts] field for more. -#### 3.2.4.1 Example +Here is a simple example. -``` json +``` js { // ... - "nnc": "NCC-1701-D" + "nnc": "_NCC-1701-D_" } ``` ### 3.2.5 Facts -FIXME not validated in the chain: do what you want! +The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. -The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. - -#### 3.2.5.1 Examples +Below is an example: ``` json { + // ... "fct": { "challenges": { "example.com": "abcdef", @@ -310,7 +324,7 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Top Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. ### 4.3.1 Reserved Abilities @@ -795,8 +809,8 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Facts] -[* gives full access] +[Facts]: #325-facts +[Top Ability]: #4312--aka-top From 7d3d9d65ce1d26500fc41075d313eea90e5d5373 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:07:08 -0700 Subject: [PATCH 034/134] INvocation --- README.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bb6ff01..ef15b67 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,7 @@ In concept there is a "bottom" ability ("none" or "void"), but it is not possibl Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensivle, but vocabularies may be reused across many Subjects. -Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation]. Caveats MUST be formatted as maps. +Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation][UCAN Invocation]. Caveats MUST be formatted as maps. On validation, the caveat array MUST be treated as a logically disjunct (`OR`). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: @@ -486,18 +486,18 @@ For instance, the following represents `({a: 1, b:2} AND {c: 3}) OR {d: 4}`: ``` js [ [ - { // ┐ - a: 1, // ├─ Caveat ─┐ - b: 2 // │ │ - }, // ┘ ├─ AND ─┐ - { // ┐ │ │ - c: 3 // ├─ Caveat ─┘ │ - } // ┘ ├─ OR - ], // │ - [ // │ - { // ┐ │ - d: 4 // ├─ Caveat ─────────┘ - } // ┘ + { // ┐ + a: 1, // ├─ Caveat ─┐ + b: 2 // │ │ + }, // ┘ ├─ AND ─┐ + { // ┐ │ │ + c: 3 // ├─ Caveat ─┘ │ + } // ┘ ├─ OR + ], // │ + [ // │ + { // ┐ │ + d: 4 // ├─ Caveat ─────────┘ + } // ┘ ] ] ``` @@ -642,7 +642,7 @@ The above MAY be expressed in compact form as follows: # 5 Validation -Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. +Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a caveat fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. @@ -691,8 +691,6 @@ const ensureProofExp = (ucan, proof) => { ## 5.2 Principal Alignment -FIXME move to high level spec - In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. This calculation MUST NOT take into account [DID fragment]s. If present, fragments are only intended to clarify which of a DID's keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired. @@ -849,6 +847,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html +[UCAN Invocation]: https://github.com/ucan-wg/invocation [UCAN]: https://github.com/ucan-wg/spec [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ [`did:ion`]: https://identity.foundation/ion/ From 878b6df49fcd216f25a996884508dfc48601e856 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:44:20 -0700 Subject: [PATCH 035/134] Improve alignment chart --- README.md | 54 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ef15b67..a6fdd25 100644 --- a/README.md +++ b/README.md @@ -697,30 +697,44 @@ This calculation MUST NOT take into account [DID fragment]s. If present, fragmen ``` mermaid flowchart RL - owner[/Alice\] -. owns .-> resource[(Storage)] - executor[/"Compute Service"\] --> del2Aud - rootIss --> owner + invoker((    Dan    \n👨)) + subject((    Alice    \n👩)) -. controls .-> resource[(Storage)] - executor -. accesses .-> resource - rootCap -. references .-> resource + rootCap -. references .-> resource - subgraph root [Root UCAN] - rootIss(iss: Alice) - rootAud(aud: Bob) - rootCap("cap: (Storage, crud/*)") - end + subgraph Delegations + subgraph root [Root UCAN] + rootIss(iss: Alice) + rootAud(aud: Bob) + rootCap("cap: (Storage, crud/*)") + end - subgraph del1 [Delegated UCAN] - del1Iss(iss: Bob) --> rootAud - del1Aud(aud: Carol) - del1Cap("cap: (Storage, crud/*)") --> rootCap - end + subgraph del1 [Delegated UCAN] + del1Iss(iss: Bob) -.-> rootAud + del1Aud(aud: Carol) + del1Cap("cap: (Storage, crud/*)") -.-> rootCap + end - subgraph del2 [Final UCAN] - del2Iss(iss: Carol) --> del1Aud - del2Aud(aud: Compute Service) - del2Cap("cap: (Storage, crud/*)") --> del1Cap - end + subgraph del2 [Delegated UCAN] + del2Iss(iss: Carol) -.-> del1Aud + del2Aud(aud: Dan) + del2Cap("cap: (Storage, crud/*)") -.-> del1Cap + end + end + + subgraph inv [Invocation] + invIss(iss: Dan) + invAud(aud: Alice) + args("args: [Storage, crud/update, (key, value)]") + prf("proofs") + end + + invIss -.-> del2Aud + invoker --> invIss + args -.-> del2Cap + invAud -.-> rootIss + rootIss --> subject + prf -.-> Delegations ``` In the above diagram, Alice has some storage. This storage may exist in one location with a single source of truth, but to help build intuition this example is location independent: local versions and remote stored copies are eventually consistent, and there is no one "correct" copy. As such, we list the owner (Alice) directly on the resource. From 3719ea30401bca3099b20bc8a53ed2f7d840eae8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:47:10 -0700 Subject: [PATCH 036/134] Fix broken Mermaid --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a6fdd25..7643b17 100644 --- a/README.md +++ b/README.md @@ -697,8 +697,8 @@ This calculation MUST NOT take into account [DID fragment]s. If present, fragmen ``` mermaid flowchart RL - invoker((    Dan    \n👨)) - subject((    Alice    \n👩)) -. controls .-> resource[(Storage)] + invoker((    Dan    )) + subject((    Alice    )) -. controls .-> resource[(Storage)] rootCap -. references .-> resource From ce22f6447d0756ddab48ad70699ac42a500b2dde Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:56:33 -0700 Subject: [PATCH 037/134] Uno mas --- README.md | 61 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 7643b17..c36afdd 100644 --- a/README.md +++ b/README.md @@ -698,43 +698,56 @@ This calculation MUST NOT take into account [DID fragment]s. If present, fragmen ``` mermaid flowchart RL invoker((    Dan    )) - subject((    Alice    )) -. controls .-> resource[(Storage)] + subject((    Alice    )) - rootCap -. references .-> resource + subject -- controls --> resource[(Storage)] + rootCap -- references --> resource subgraph Delegations - subgraph root [Root UCAN] - rootIss(iss: Alice) - rootAud(aud: Bob) - rootCap("cap: (Storage, crud/*)") - end - - subgraph del1 [Delegated UCAN] - del1Iss(iss: Bob) -.-> rootAud - del1Aud(aud: Carol) - del1Cap("cap: (Storage, crud/*)") -.-> rootCap - end - - subgraph del2 [Delegated UCAN] - del2Iss(iss: Carol) -.-> del1Aud - del2Aud(aud: Dan) - del2Cap("cap: (Storage, crud/*)") -.-> del1Cap - end + subgraph root [Root UCAN] + subgraph rooting [Root Issuer] + rootIss(iss: Alice) + rootSub(sub: Alice) + end + + rootCap("cap: (Storage, crud/*)") + rootAud(aud: Bob) + end + + subgraph del1 [Delegated UCAN] + del1Iss(iss: Bob) --> rootAud + del1Sub(sub: Alice) + del1Aud(aud: Carol) + del1Cap("cap: (Storage, crud/*)") --> rootCap + + + del1Sub --> rootSub + end + + subgraph del2 [Delegated UCAN] + del2Iss(iss: Carol) --> del1Aud + del2Sub(sub: Alice) + del2Aud(aud: Dan) + del2Cap("cap: (Storage, crud/*)") --> del1Cap + + del2Sub --> del1Sub + end end subgraph inv [Invocation] invIss(iss: Dan) - invAud(aud: Alice) args("args: [Storage, crud/update, (key, value)]") + invSub(sub: Alice) prf("proofs") end - invIss -.-> del2Aud + invIss --> del2Aud invoker --> invIss - args -.-> del2Cap - invAud -.-> rootIss + args --> del2Cap + invSub --> del2Sub rootIss --> subject - prf -.-> Delegations + rootSub --> subject + prf --> Delegations ``` In the above diagram, Alice has some storage. This storage may exist in one location with a single source of truth, but to help build intuition this example is location independent: local versions and remote stored copies are eventually consistent, and there is no one "correct" copy. As such, we list the owner (Alice) directly on the resource. From cd4fdf523b9db703ed997d9a2cb37444b41b077c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 16:58:30 -0700 Subject: [PATCH 038/134] MIssing link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c36afdd..3fc3e46 100644 --- a/README.md +++ b/README.md @@ -871,6 +871,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Protocol Labs]: https://protocol.ai/ [RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 [RS256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.3 +[Raw data multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L41 [SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html From 810d681273205b24fe229183f564ff5b7602cca3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 17:02:15 -0700 Subject: [PATCH 039/134] Port missing .github dir (oops!) --- .github/CODEOWNERS | 5 +++++ .github/workflows/linkcheck.yml | 14 ++++++++++++++ .github/workflows/spellcheck.yml | 12 ++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/linkcheck.yml create mode 100644 .github/workflows/spellcheck.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..628788e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @expede @matheus23 @gozala @dholms diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml new file mode 100644 index 0000000..b25de1f --- /dev/null +++ b/.github/workflows/linkcheck.yml @@ -0,0 +1,14 @@ +name: Check Markdown links + +on: push + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: 'yes' + check-modified-files-only: 'yes' + base-branch: 'main' diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml new file mode 100644 index 0000000..06710ca --- /dev/null +++ b/.github/workflows/spellcheck.yml @@ -0,0 +1,12 @@ +name: Spellcheck Action +on: push + +jobs: + build: + name: Spellcheck + runs-on: ubuntu-latest + steps: + # The checkout step + - uses: actions/checkout@master + - uses: rojopolis/spellcheck-github-actions@0.24.0 + name: Spellcheck From ec81ddeceea35d2d4de8227778a0c8996bacd4f7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 17:07:18 -0700 Subject: [PATCH 040/134] Add missing configs --- .clabot | 15 ++++ .custom-words.txt | 180 ++++++++++++++++++++++++++++++++++++++++++++++ .spellcheck.yml | 26 +++++++ 3 files changed, 221 insertions(+) create mode 100644 .clabot create mode 100644 .custom-words.txt create mode 100644 .spellcheck.yml diff --git a/.clabot b/.clabot new file mode 100644 index 0000000..d504977 --- /dev/null +++ b/.clabot @@ -0,0 +1,15 @@ +{ + "contributors": [ + "appcypher", + "b5", + "bgins", + "expede", + "matheus23", + "QuinnWilton", + "bmann", + "dholms", + "gozala", + "gobengo", + "nicobao" + ] +} diff --git a/.custom-words.txt b/.custom-words.txt new file mode 100644 index 0000000..dd68f4a --- /dev/null +++ b/.custom-words.txt @@ -0,0 +1,180 @@ +ACL +ACLs +AlicePhone +AliceRoot +Attenuations +Aud +Auth +AuthorityA +AuthorityB +BCP +BT +Bluesky +BxZ +CIDs +CIDv +CNAME +CRDT +CRDTs +Canonicalization +Compositionality +DAG +DAGs +DHT +DID's +DIDs +DNS +DNSLinks +DT +Datalog +ECDSA +EL +EdDSA +GLVVQR +GUID +Golang +Gozalishvili +HEHYSF +Haus +Holmgren +Invoker's +Irakli +JSON +JWT +JWTs +Karp +Kleppmann +Krüger +LD +Lemmer +Meiklejohn +Memoized +Merkle +Mikael +MkiTBz +OCAP +OCapN +PITM +PKI +POLA +Philipp +RBAC +RESTful +RL +RSA +RSM +Redelegating +Redelegation +Revoker +SDSI +SHA +SPKI +ScopeA +ScopeB +Seitan +Subschemes +TXT +TypeScript +UCAN +UCAN's +UCANs +URI +URIs +Vandevelde +WG +WebNative +Webber +ZCAP +Zelenka +adoptability +al +alice +attenuations +aud +auth +autonumber +bene +blockchain +codec +codecs +crudGraph +cryptographically +del +delegable +delegator +dereference +disambiguates +disjunct +djdX +dns +enums +et +extractable +filesystem +fooey +hawaii +init +inlining +interconnectivity +interpretable +inv +invoker +iss +js +json +lifecycle +lockboxes +mDooWp +mailto +matcher +memoization +modelled +msg +msgGraph +multicodec +namespace +namespaced +namespaces +nonces +nota +ocap +pcec +plaintext +potencies +pre +preimages +quG +rc +reauthorizations +recommentations +redelegate +redelegates +requestors +rootAud +rootCap +rootIss +runtime +scalable +sdarnit +sequenceDiagram +situationally +stateful +subdelegate +subgraph +subscheme +tradeoffs +trustless +trustlessly +ucan +un +unary +unforgeability +unresolvable +url +validatable +validator +validator's +verifiability +wnfs +xyz +ymuepAQ diff --git a/.spellcheck.yml b/.spellcheck.yml new file mode 100644 index 0000000..12935cb --- /dev/null +++ b/.spellcheck.yml @@ -0,0 +1,26 @@ +matrix: +- name: Markdown + aspell: + lang: en + d: en_US + dictionary: + encoding: utf-8 + wordlists: [".custom-words.txt"] + pipeline: + - pyspelling.filters.markdown: + - pyspelling.filters.html: + comments: false + ignores: + - code + - pre + - pyspelling.filters.context: + context_visible_first: true + escapes: \\[\\`~] + delimiters: + - open: '(?s)^(?P *`{3,})$' + close: '^(?P=open)$' + - open: '(?P`+)' + close: '(?P=open)' + sources: + - 'README.md' + default_encoding: utf-8 From 48d72d183a49df0cedd653cdbbcc8caadd5f491f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 17:12:09 -0700 Subject: [PATCH 041/134] Add another case to caveat delegation truth table --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3fc3e46..1588481 100644 --- a/README.md +++ b/README.md @@ -785,6 +785,7 @@ Here are some abstract cases given in [normal form]. | `[[{a: 1}]]` | `[[{b: 2}]]` | No | Escalation by using a different caveat | | `[{a: 1}], [{b: 2}]]` | `[[{a: 1}]]` | Yes | Removes a capability (removes an `OR` branch) | | `[[{a: 1}], [{b: 2}]]` | `[[{a: 1}], [{b: 2, c: 3}]]` | Yes | Attenuates existing caveat | +| `[[{a: 1}]]` | `[[{a: 1}, {a: 2}]]` | Yes | Adds new caveat inside an `AND` block | | `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]]` | No | Escalation by adding new capability | | `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]` | No | Escalation by adding new capability (`{b: 2}`) | | `[[{a: 1}]]` | `[[{a: 1, b: 2}], [{a: 1, c: 3}]]` | Yes | Attenuates the original capability | From eab662ac95599f63610cf764b6afd627f13de245 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 17:13:16 -0700 Subject: [PATCH 042/134] Missing link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1588481..191d897 100644 --- a/README.md +++ b/README.md @@ -883,6 +883,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [`did:plc`]: https://github.com/did-method-plc/did-method-plc [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 +[dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L112 [did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa From bcfaad49bc1696b43f9fba6a7d2576ab8b01f9ac Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 17:15:54 -0700 Subject: [PATCH 043/134] Add misisng lnks --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 191d897..29cf40d 100644 --- a/README.md +++ b/README.md @@ -867,6 +867,8 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Mark Miller]: https://github.com/erights [Martin Kleppmann]: https://martin.kleppmann.com/ [Mikael Rogers]: https://github.com/mikeal/ +[OCAP]: https://en.wikipedia.org/wiki/Object-capability_model +[OCapN]: https://github.com/ocapn/ [Philipp Krüger]: https://github.com/matheus23 [PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [Protocol Labs]: https://protocol.ai/ @@ -876,8 +878,10 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html +[Steven Vandevelde]: https://github.com/icidasset [UCAN Invocation]: https://github.com/ucan-wg/invocation [UCAN]: https://github.com/ucan-wg/spec +[W3C]: https://www.w3.org/ [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ [`did:ion`]: https://identity.foundation/ion/ [`did:key`]: https://w3c-ccg.github.io/did-method-key/ @@ -888,3 +892,4 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems +[ucan.xyz]: https://ucan.xyz From f4d1bcc6438661938ae3ca7af3f112835a4d075b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 20:42:34 -0700 Subject: [PATCH 044/134] Configure linkchecker --- .github/workflows/linkcheck.cfg.json | 10 ++++++++ .github/workflows/linkcheck.yml | 22 +++++++++++------ README.md | 36 +++++++++------------------- 3 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/linkcheck.cfg.json diff --git a/.github/workflows/linkcheck.cfg.json b/.github/workflows/linkcheck.cfg.json new file mode 100644 index 0000000..1d3eb1e --- /dev/null +++ b/.github/workflows/linkcheck.cfg.json @@ -0,0 +1,10 @@ +{ + "ignorePatterns": [ + { + "pattern": "https://share.ansi.org/Shared%20Documents/Standards%20Activities/American%20National%20Standards/Procedures,%20Guides,%20and%20Forms/2020_ANSI_Essential_Requirements.pdf" + }, + { + "pattern": "https://share.ansi.org/Shared%20Documents/Standards%20Activities/American%20National%20Standards/Procedures,%20Guides,%20and%20Forms/2020_ANSI_Essential_Requirements.pdf" + } + ] +} diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index b25de1f..10fa532 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -1,14 +1,22 @@ name: Check Markdown links -on: push +on: + push: + branches: + - main + pull_request: jobs: markdown-link-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - use-quiet-mode: 'yes' - check-modified-files-only: 'yes' - base-branch: 'main' + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: "yes" + check-modified-files-only: "yes" + base-branch: "main" + config-file: "./.github/workflows/linkcheck.cfg.json" + - uses: DavidAnson/markdownlint-cli2-action@v9 + with: + globs: "README.md" diff --git a/README.md b/README.md index 29cf40d..c939728 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. ### 4.3.1 Reserved Abilities -#### 43.1.1 `ucan` Namespace +#### 4.3.1.1 `ucan` Namespace The `ucan` abilty namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` @@ -378,10 +378,6 @@ flowchart BT ... --> * ``` -#### 4.3.1.3 Bottom - -In concept there is a "bottom" ability ("none" or "void"), but it is not possible to represent in an ability. As it is merely the absence of any ability, it is not possible to construct a capability with a bottom ability. - ## 4.4 Caveats Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensivle, but vocabularies may be reused across many Subjects. @@ -437,36 +433,25 @@ The above MUST be interpreted as the set of capabilities below in the following When validating a delegation chain in the abstract, all caveats MUST be present in each sucessive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. Note that all caveats need to be understable to th execitor -### 4.4.1 All or Nothing - -Caveats MAY include user-supplied fields, but there are two default cases that MUST be supported. These are called the "top" and "bottom" caveats. +### 4.4.1 The "True" Caveat -#### 4.4.1.1 The Top Caveat - -The top caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[[{}]]`. In compact form, the caveat array MAY be omitted. +The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[[{}]]`. In compact form, the caveat array MAY be omitted. ``` js -// Normal Form +// True Caveat in Normal Form { - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "did:plc:ewvi7nxzyoun6zhxrhs64oiz": { "some/ability": [[{}]] } } -// Compact Form -{ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "some/ability", -} -``` - -#### 4.4.1.2 The Bottom Caveat - -The explicitly empty caveat (`[[]]`) MUST represent disallowing any action on the resource. In predicate logic terms, this represents `false`. While possible to express with the bottom caveat, it is equivalent to simply omitting the affected branches or capabilities. For reasons of legibility and size, omission is RECOMMENDED. - -``` js +// Valid Compact Forms of the True Caveat { + "did:plc:ewvi7nxzyoun6zhxrhs64oiz": "some/ability", "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "some/ability": [[]] // Equivalent to omitting this ability + "foo/*": {} + "bar/*": [] + "baz/*": [{}] } } ``` @@ -837,6 +822,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Facts]: #325-facts [Top Ability]: #4312--aka-top +[noral form] From 6dc4d8aba5beae23901ad0a42ca85e4d999726b5 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:28:40 -0700 Subject: [PATCH 045/134] Wildcard --- .custom-words.txt | 44 +++++++++++++++++++++++++++++++++ README.md | 63 +++++++++++++++++++++++++---------------------- 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/.custom-words.txt b/.custom-words.txt index dd68f4a..ff1d0f3 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -23,6 +23,7 @@ DAGs DHT DID's DIDs +DNF DNS DNSLinks DT @@ -35,6 +36,7 @@ GUID Golang Gozalishvili HEHYSF +HMACs Haus Holmgren Invoker's @@ -58,6 +60,7 @@ PITM PKI POLA Philipp +Pseudocode RBAC RESTful RL @@ -89,14 +92,21 @@ Zelenka adoptability al alice +async attenuations aud auth autonumber +baz bene blockchain +cid codec codecs +const +const +const +const crudGraph cryptographically del @@ -107,12 +117,26 @@ disambiguates disjunct djdX dns +ensureProofExp +ensureProofExp +ensureProofNbf +ensureProofNbf +ensureTime +ensureTimeBounds +ensureTimeBounds enums +escelation et +ewvi +ewvi extractable filesystem fooey +forEach +getUcan hawaii +hoc +https init inlining interconnectivity @@ -135,14 +159,29 @@ multicodec namespace namespaced namespaces +nbf +nbf +nbsp +nbspAlice +nbspDan nonces nota +num +numerics +nxzyoun +nxzyoun ocap +oiz +oiz +pathing pcec plaintext +plc +plc potencies pre preimages +prf quG rc reauthorizations @@ -157,6 +196,7 @@ runtime scalable sdarnit sequenceDiagram +significand situationally stateful subdelegate @@ -170,11 +210,15 @@ un unary unforgeability unresolvable +uri url validatable validator validator's +validators verifiability wnfs xyz ymuepAQ +zhxrhs +zhxrhs diff --git a/README.md b/README.md index c939728..fefc852 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 0 Abstract -UCAN Delegation is a component of [UCAN]. This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilties, heirarchical authority, and extensible caveats. +UCAN Delegation is a component of [UCAN]. This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible caveats. # 1 Introduction @@ -31,13 +31,14 @@ UCAN Delegation is a component of [UCAN]. This specification describes the seman Design goals: -- Flexible pathing -- Extensibilty -- Consistency for interop +- Flexible delegation paths +- Semantic extensibility +- Ad hoc caveats +- Consistency for interoperability # 2 Token Structure -Regardless of how a Delegation is serialized on the wire, the sigature of a UCAN Delegation MUST be over a payload serialized as a [JWT] . +Regardless of how a Delegation is serialized on the wire, the signature of a UCAN Delegation MUST be over a payload serialized as a [JWT] . The overall container of a header, claims, and signature remain as per [RFC 7519][JWT]. @@ -80,13 +81,13 @@ The payload MUST describe the authorization claims, who is involved, and its val | Field | Type | Description | Required | |-------|-------------------------------------------|--------------------------------------------------------|----------| | `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | -| `iss` | `String` | Issuer DID (sender) | Yes | -| `aud` | `String` | Audience DID (receiver) | Yes | +| `iss` | `DID` | Issuer DID (sender) | Yes | +| `aud` | `DID` | Audience DID (receiver) | Yes | | `nbf` | `Integer` (53-bits[^js-num-size]) | Not Before UTC Unix Timestamp in seconds (valid from) | No | | `exp` | `Integer \| null` (53-bits[^js-num-size]) | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | | `nnc` | `String` | Nonce | Yes | | `fct` | `{String: Any}` | Facts (asserted, signed data) | No | -| `cap` | `{URI: {Ability: Caveat}}` | Capabilities | Yes | +| `cap` | `{DID: {Ability: Caveat}}` | Capabilities | Yes | ### 3.2.1 Version @@ -100,7 +101,7 @@ The `iss` and `aud` fields MUST contain a single principal each. If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:ion`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. -[EdDSA] `did:key`s MUST be suppoted, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. +[EdDSA] `did:key`s MUST be supported, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. Note that every [Subject] MUST correspond to a root delegation issuer. @@ -169,7 +170,7 @@ Below are a couple examples: The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. -The recommeneded size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's cryptosuite. +The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's crypto suite. This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`fct`][Facts] field for more. @@ -212,9 +213,11 @@ Capabilities are the semantically-relevant claims of a delegation. They MUST be ``` This map MUST contain some or none of the following: -1. A strict subset (attenuation) of the capability authority from the next direct `prf` field -2. Capabilities composed from multiple proofs (see [rights amplification]) -3. Capabilities originated by the `iss` DID (i.e. by parenthood) + +0. Nothing +1. Capabilities unchanged from a previous delegation +2. A strict subset (attenuation) of the capability authority from the next direct `prf` field +3. Capabilities about a Subject that matches the Issuer (`iss`) DID The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. @@ -324,32 +327,32 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Top Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. ### 4.3.1 Reserved Abilities #### 4.3.1.1 `ucan` Namespace -The `ucan` abilty namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` +The `ucan` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` Support for the `ucan/*` delegated proof ability is RECOMMENDED. -#### 4.3.1.2 `*` AKA "Top" +#### 4.3.1.2 `*` AKA "Wildcard" -_"Top" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ +_"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ -The "top" (or "any") ability MUST be denoted `*`. This can be thought of as something akin to a super user permission in RBAC. +The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be thought of as something akin to a super user permission in RBAC. ``` js { - // ... Top + // ... Wildcard "cap": { //┌┴┐ "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "*" } } ``` -The top ability grants access to all other capabilities for the specified resource, across all possible namespaces. The top ability is useful when "linking" agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delgate to update a Subject's mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others. +The wildcard ability grants access to all other capabilities for the specified resource, across all possible namespaces. The wildcard ability is useful when "linking" agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delegate to update a Subject's mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others. ``` mermaid %%{ init: { 'flowchart': { 'curve': 'linear' } } }%% @@ -380,7 +383,7 @@ flowchart BT ## 4.4 Caveats -Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensivle, but vocabularies may be reused across many Subjects. +Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation][UCAN Invocation]. Caveats MUST be formatted as maps. @@ -430,8 +433,8 @@ The above MUST be interpreted as the set of capabilities below in the following | `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | | `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | -When validating a delegation chain in the abstract, all caveats MUST be present in each sucessive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. -Note that all caveats need to be understable to th execitor +When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. +Note that all caveats need to be understable to th executor ### 4.4.1 The "True" Caveat @@ -458,11 +461,11 @@ The "True" caveat MUST represent the lack of caveat. In predicate logic terms, i ### 4.4.2 Serialization -Caveats have two isomorphic serilaizations: [compact form] and [normal form]. It is often easiest for validators to expand to normal form prior to checking. Compact form is more easily legible and requires fewer bytes. +Caveats have two isomorphic serializations: [compact form] and [normal form]. It is often easiest for validators to expand to normal form prior to checking. Compact form is more easily legible and requires fewer bytes. ### 4.4.2.1 Normal Form -Caveats MAY be expressed in a compact form, but any caveat MUST be expressable in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. +Caveats MAY be expressed in a compact form, but any caveat MUST be expressible in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. Normal form MUST take the following shape: `[[{}]]`. The outer array represents a logical `any` (chained `OR`s), and the inner arrays represent logical `all` (chained `AND`s). @@ -561,7 +564,7 @@ Note that while adding whole objects is useful in many situation as above, atten ### 4.4.2.1 Compact Form -Normal from is consistent, but needlessly verbose for simple cases. Compact form omits superflous branches. Consider the following normal form capabilities: +Normal from is consistent, but needlessly verbose for simple cases. Compact form omits superfluous branches. Consider the following normal form capabilities: ``` json { @@ -663,13 +666,13 @@ const ensureTimeBounds = (ucan) => { const ensureProofNbf = (ucan, proof) => { if (!!proof.nbf && ucan.nbf < proof.nbf) { - throw new Error("Time escelation: delegation starts before proof starts") + throw new Error("Time escalation: delegation starts before proof starts") } } const ensureProofExp = (ucan, proof) => { if (proof.exp !== null && ucan.exp > proof.exp) { - throw new Error("Time escelation: delegation ends after proof ends") + throw new Error("Time escalation: delegation ends after proof ends") } } ``` @@ -821,8 +824,8 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Facts]: #325-facts -[Top Ability]: #4312--aka-top -[noral form] +[Wildcard Ability]: #4312--aka-wildcard +[normal form]: $4421-normal-form From 29150b6d1618c9a44533c7b40d8fc63b017baaa6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:32:56 -0700 Subject: [PATCH 046/134] Fix link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fefc852..293e41e 100644 --- a/README.md +++ b/README.md @@ -825,7 +825,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Facts]: #325-facts [Wildcard Ability]: #4312--aka-wildcard -[normal form]: $4421-normal-form +[normal form]: #4421-normal-form @@ -872,7 +872,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [UCAN]: https://github.com/ucan-wg/spec [W3C]: https://www.w3.org/ [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ -[`did:ion`]: https://identity.foundation/ion/ +[`did:ion`]: https://identity.foundation/ion [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [`did:plc`]: https://github.com/did-method-plc/did-method-plc [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 From 13545806c114c367a984a869360127bbb6c9cdcc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:34:12 -0700 Subject: [PATCH 047/134] Spelling --- .custom-words.txt | 2 ++ README.md | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.custom-words.txt b/.custom-words.txt index ff1d0f3..9e37797 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -108,6 +108,7 @@ const const const crudGraph +crypto cryptographically del delegable @@ -129,6 +130,7 @@ escelation et ewvi ewvi +extensibility extractable filesystem fooey diff --git a/README.md b/README.md index 293e41e..2997359 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,7 @@ flowchart BT ## 4.4 Caveats -Caveats define a set of constraints on what can be redelegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. +Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation][UCAN Invocation]. Caveats MUST be formatted as maps. @@ -434,7 +434,8 @@ The above MUST be interpreted as the set of capabilities below in the following | `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. -Note that all caveats need to be understable to th executor + +Note that all caveats need to be understood by the Executor. ### 4.4.1 The "True" Caveat From 3b6d402493385b21de6a49528690d6f25d5a7214 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:40:54 -0700 Subject: [PATCH 048/134] ION keeps 403ing, so did:web it is! --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2997359..0291b7f 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The `iss` and `aud` fields describe the token's principals. They are distinguish The `iss` and `aud` fields MUST contain a single principal each. -If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:ion`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. +If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:web`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. [EdDSA] `did:key`s MUST be supported, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. @@ -113,13 +113,13 @@ Below are a couple examples: ``` ```json -"aud": "did:ion:EiCrsG_DLDmSKic1eaeJGDtUoC1dj8tj19nTRD9ODzAjaQ", +"aud": "did:web:example.com", "iss": "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", ``` ```json "aud": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", -"iss": "did:ion:test:EiANCLg1uCmxUR4IUkpW8Y5_nuuXLbAEwonQd4q8pflTnw#key-1", +"iss": "did:web:example.com", ``` ```json @@ -873,7 +873,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [UCAN]: https://github.com/ucan-wg/spec [W3C]: https://www.w3.org/ [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ -[`did:ion`]: https://identity.foundation/ion +[`did:web`]: https://w3c-ccg.github.io/did-method-web/ [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [`did:plc`]: https://github.com/did-method-plc/did-method-plc [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 From 4817389a77b45cb82cf2904754cccb8597a975a9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:44:05 -0700 Subject: [PATCH 049/134] LOL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0291b7f..4090000 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati } ``` -In the case where access to an [external resource] is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Caveat](s), except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor. +In the case where access to an [external resource] is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Caveat(s)][Caveat], except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor. ``` js { From 98b135248cca3de6b0968d4d4337a9248ee8c863 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:45:44 -0700 Subject: [PATCH 050/134] don't markdown lint --- .github/workflows/linkcheck.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index 10fa532..b5a0a32 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -17,6 +17,3 @@ jobs: check-modified-files-only: "yes" base-branch: "main" config-file: "./.github/workflows/linkcheck.cfg.json" - - uses: DavidAnson/markdownlint-cli2-action@v9 - with: - globs: "README.md" From 03bfc6eb9510bfeb4e33771706985cd6da0b690d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:55:19 -0700 Subject: [PATCH 051/134] Many missing links --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4090000..c47f066 100644 --- a/README.md +++ b/README.md @@ -785,7 +785,7 @@ A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMME The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other codecs are used (such as [`0x0129` `dag-json` multicodec][dag-json multicodec]), the UCAN MUST be able to be interpreted as a valid JWT (including the signature). -The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. Please refer to [token resolution] for more. +The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. ## 6.1 CID Canonicalization @@ -824,8 +824,11 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[Caveat]: #44-caveats [Facts]: #325-facts +[Subject]: #41-subject [Wildcard Ability]: #4312--aka-wildcard +[compact form]: #4421-compact-form [normal form]: #4421-normal-form @@ -840,6 +843,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata +[DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ [DNF]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [Dan Finlay]: https://github.com/danfinlay @@ -849,7 +853,9 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Executor]: https://github.com/ucan-wg/spec#31-roles [Fission]: https://fission.codes [Hugo Dias]: https://github.com/hugomrdias +[IEEE-754]: https://ieeexplore.ieee.org/document/8766229 [Ink & Switch]: https://www.inkandswitch.com/ +[Invocation]: https://github.com/ucan-wg/invocation [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number [JWT]: https://www.rfc-editor.org/rfc/rfc7519 @@ -859,9 +865,11 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Mikael Rogers]: https://github.com/mikeal/ [OCAP]: https://en.wikipedia.org/wiki/Object-capability_model [OCapN]: https://github.com/ocapn/ +[Operation]: https://github.com/ucan-wg/spec#33-operation [Philipp Krüger]: https://github.com/matheus23 [PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [Protocol Labs]: https://protocol.ai/ +[RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339 [RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 [RS256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.3 [Raw data multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L41 @@ -873,13 +881,14 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [UCAN]: https://github.com/ucan-wg/spec [W3C]: https://www.w3.org/ [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ -[`did:web`]: https://w3c-ccg.github.io/did-method-web/ [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [`did:plc`]: https://github.com/did-method-plc/did-method-plc +[`did:web`]: https://w3c-ccg.github.io/did-method-web/ [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 [dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L112 [did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems +[revocation]: https://github.com/ucan-wg/revocation [ucan.xyz]: https://ucan.xyz From c07eb86220f2ac6c950162216f474660e8f728cb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 12 Oct 2023 21:57:49 -0700 Subject: [PATCH 052/134] Consistent rendering --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c47f066..33ff921 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowled Below is an example: -``` json +``` js { // ... "fct": { From c49bc95d66d1d97256a7ef144cf3d2797835ee92 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Fri, 13 Oct 2023 23:16:31 -0700 Subject: [PATCH 053/134] JWT -> IPLD --- README.md | 464 ++++++++++++++++++------------------------------------ 1 file changed, 154 insertions(+), 310 deletions(-) diff --git a/README.md b/README.md index 33ff921..d0ad7af 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,40 @@ # UCAN Delegation Specification v1.0.0-rc.1 +FIXME IPLD? + +``` +type Signed struct { + del &Delegation + sig Signature +} + +type Delegation struct { + ucd Semver + + iss DID + aud DID + + exp Integer + nbf Integer + + sub DID + aby String + cav [{String : Any}] +} + +type Signature union { + | batch &BatchSig + | inline Bytes +} + +type BatchSig struct { + scp [&Any] + sig Bytes +} + +FIXME if you used a merkle tree, you could embed the merkle proof inside the payload, but then it's really tied down +``` + ## Editors * [Brooklyn Zelenka], [Fission] @@ -15,7 +50,7 @@ * [UCAN] * [DID] -* [JWT] +* [DSG-CBOR] ## Language @@ -38,44 +73,6 @@ Design goals: # 2 Token Structure -Regardless of how a Delegation is serialized on the wire, the signature of a UCAN Delegation MUST be over a payload serialized as a [JWT] . - -The overall container of a header, claims, and signature remain as per [RFC 7519][JWT]. - -## 3.1 Header - -The header is a standard JWT header, and MUST include all of the following fields: - -| Field | Type | Description | Required | -|-------|----------|------------------------|----------| -| `alg` | `String` | Signature algorithm | Yes | -| `typ` | `String` | Type (MUST be `"JWT"`) | Yes | - -### 3.1.1 Algorithms - -The following algorithms are RECOMMENDED to be validatable: - -- [ES256] -- [EdDSA][RFC 8037] -- [RS256] - -All algorithms MUST match the DID principal in the `iss` field. This enforces that the `alg` field MUST be asymmetric (public key cryptography or nonstandard but emerging patterns like smart contract signatures) - -The JWT `"alg": "none"` MUST NOT be supported, as the lack of signature prevents the issuer DID from being validated. - -Symmetric algorithms such as HMACs (e.g. `"alg": "HS256"`) MUST NOT be supported, since they cannot be used to prove control over the issuer DID. - -Here is a common example: - -```json -{ - "alg": "EdDSA", - "typ": "JWT" -} -``` - -## 3.2 Payload - The payload MUST describe the authorization claims, who is involved, and its validity period. | Field | Type | Description | Required | @@ -89,11 +86,11 @@ The payload MUST describe the authorization claims, who is involved, and its val | `fct` | `{String: Any}` | Facts (asserted, signed data) | No | | `cap` | `{DID: {Ability: Caveat}}` | Capabilities | Yes | -### 3.2.1 Version +## 2.1 Version The `ucv` field sets the version of the UCAN specification used in the payload. -### 3.2.2 Principals +## 2.2 Principals The `iss` and `aud` fields describe the token's principals. They are distinguished by having DIDs. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. @@ -127,7 +124,7 @@ Below are a couple examples: "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", ``` -### 3.2.3 Time Bounds +## 2.3 Time Bounds `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 MUST represent seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. @@ -166,7 +163,7 @@ Below are a couple examples: } ``` -### 3.2.4 Nonce +## 2.4 Nonce The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. @@ -183,7 +180,7 @@ Here is a simple example. } ``` -### 3.2.5 Facts +## 2.5 Facts The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. @@ -204,12 +201,12 @@ Below is an example: } ``` -# 4. Capabilities +# 3. Capabilities Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: ``` -{ $SUBJECT: { $ABILITY: $CAVEATS } } +{ $SUBJECT: { $ABILITY: [$CAVEAT] } } ``` This map MUST contain some or none of the following: @@ -226,23 +223,21 @@ Here is an illustrative example: ``` js { // ... - "cap": { - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "crud/create": { - "uri": "https://blog.example.com/blog/", - "status": "draft" - }, - "msg/send": { - "sender": "mailto:alice@example.com", - "subject": "Weekly Reports", - "day": "friday" - } + sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" + can: "msg/send", + cav: [ + { + "sender": "mailto:alice@example.com", + "day": "friday" + }, + { + "subject": "Weekly Reports", } - } + ] } ``` -## 4.1 Subject +## 3.1 Subject The Subject MUST be the DID that initiated the delegation chain. @@ -250,16 +245,12 @@ For example: ``` js { + sub: "did:web:example.com", // ... - cap: { - "did:web:example.com": "ucan/*" - //└─────────┬─────────┘ - // Subject - } } ``` -## 4.2 Resource +## 3.2 Resource Unlike Subjects and Abilities, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. @@ -267,16 +258,10 @@ By default, the Resource of a capability is the Subject. This makes the delegati ``` js { + sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource + can: "crud/update", + cav: [{status: "draft"}] // ... - "cap": { - // Subject & Resource - //┌───────────────────────────┴────────────────────────────┐ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "crud/update": { - "status": "draft" - } - } - } } ``` @@ -284,60 +269,47 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` js { - // ... - "cap": { - // Subject - //┌───────────────────────────┴────────────────────────────┐ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "crud/create": { - "uri": "https://blog.example.com/blog/", - // └──────────────┬───────────────┘ - // Resource - "status": "draft" - }, - "msg/send": { - "sender": "mailto:alice@example.com", - // └───────────┬────────────┘ - // Resource - "subject": "Weekly Reports", - "day": "friday" - } + sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", + can: "crud/create", + cav: [ + { + "uri": "https://example.com/blog/", // Resource + "status": "draft" } - } + ], + // ... } ``` -## 4.3 Abilities +## 3.3 Abilities Abilities MUST be presented as a case-insensitive string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. ``` js { - // ... - "cap": { - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - // Ability - // ┌─────┴─────┐ - "crud/create": { - "uri": "https://blog.example.com/blog/", - "status": "draft" - } + sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + can: "msg/send", + cav: [ + { + "from": "mailto:alice@example.com", + "to": "mailto:bob@example.com" } - } + ], + // ... } ``` Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. -### 4.3.1 Reserved Abilities +### 3.3.1 Reserved Abilities -#### 4.3.1.1 `ucan` Namespace +#### 3.3.1.1 `ucan` Namespace The `ucan` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` Support for the `ucan/*` delegated proof ability is RECOMMENDED. -#### 4.3.1.2 `*` AKA "Wildcard" +#### 3.3.1.2 `*` AKA "Wildcard" _"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ @@ -345,10 +317,8 @@ The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be tho ``` js { - // ... Wildcard - "cap": { //┌┴┐ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "*" - } + can: "*", + // ... } ``` @@ -381,7 +351,7 @@ flowchart BT ... --> * ``` -## 4.4 Caveats +## 3.4 Caveats Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. @@ -391,47 +361,27 @@ On validation, the caveat array MUST be treated as a logically disjunct (`OR`). ``` js { - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", - "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": { - "crud/create": { - "uri": "dns:example.com", // ┐ - "record": "TXT" // ├─ Caveat - } // ┘ - }, - "did:web:example.com": { - "crud/read": { - "uri": "https://blog.example.com", // ┐ - "status": "published" // ├─ Caveat - }, // ┘ - "crud/update": [ - { // ┐ - "uri": "https://example.com/newsletter/", // ├─ Caveat - "status": "draft" // │ - }, // ┘ - [ - { // ┐ - "uri": "https://blog.example.com", // │ - "status": "published" // ├─ Caveat - "tag": "news", // │ - }, // ┘ - { // ┐ - "tag": "breaking" // ├─ Caveat - } // ┘ - ] - ] - } + sub: "did:web:example.com", + can: "crud/update", + cav: [ + { // ┐ + "uri": "https://blog.example.com", // │ + "status": "published" // ├─ Caveat + "tag": "news", // │ + }, // ┘ + { // ┐ + "tag": "breaking" // ├─ Caveat + } // ┘ + ], + // ... } ``` The above MUST be interpreted as the set of capabilities below in the following table: -| Subject | Ability | Caveat | -|------------------------------------------------------------|---------------|--------------------------------------------------------------------------------------------------------| -| `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` | `ucan/*` | Always | -| `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` | `crud/create` | `TXT` records on `dns:example.com` | -| `did:web:example.com` | `crud/read` | Posts at `https://blog.example.com` with a `published` status | -| `did:web:example.com` | `crud/update` | Posts at `https://example.com/newsletter/` with the `draft` status | -| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | +| Subject | Ability | Caveat | +|-----------------------|---------------|--------------------------------------------------------------------------------------------------------| +| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. @@ -439,55 +389,40 @@ Note that all caveats need to be understood by the Executor. ### 4.4.1 The "True" Caveat -The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[[{}]]`. In compact form, the caveat array MAY be omitted. +The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[{}]`, but is equivalent to `[]`. ``` js -// True Caveat in Normal Form { - "did:plc:ewvi7nxzyoun6zhxrhs64oiz": { - "some/ability": [[{}]] - } + cav: [{}], + // ... } -// Valid Compact Forms of the True Caveat { - "did:plc:ewvi7nxzyoun6zhxrhs64oiz": "some/ability", - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "foo/*": {} - "bar/*": [] - "baz/*": [{}] - } + cav: [], + // ... } ``` -### 4.4.2 Serialization - -Caveats have two isomorphic serializations: [compact form] and [normal form]. It is often easiest for validators to expand to normal form prior to checking. Compact form is more easily legible and requires fewer bytes. - ### 4.4.2.1 Normal Form Caveats MAY be expressed in a compact form, but any caveat MUST be expressible in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. -Normal form MUST take the following shape: `[[{}]]`. The outer array represents a logical `any` (chained `OR`s), and the inner arrays represent logical `all` (chained `AND`s). +Normal form MUST take the following shape: `[{}]`. The arrays represents logical `all` (chained `AND`s). To represent logical `any` / `OR`, issue another delegation with that attenuation. -For instance, the following represents `({a: 1, b:2} AND {c: 3}) OR {d: 4}`: +For instance, the following represents `({a: 1, b:2} AND {c: 3}) AND {d: 4}`: ``` js [ - [ - { // ┐ - a: 1, // ├─ Caveat ─┐ - b: 2 // │ │ - }, // ┘ ├─ AND ─┐ - { // ┐ │ │ - c: 3 // ├─ Caveat ─┘ │ - } // ┘ ├─ OR - ], // │ - [ // │ - { // ┐ │ - d: 4 // ├─ Caveat ─────────┘ - } // ┘ - ] + { // ┐ + a: 1, // ├─ Caveat ─┐ + b: 2 // │ │ + }, // ┘ ├─ AND ─┐ + { // ┐ │ │ + c: 3 // ├─ Caveat ─┘ │ + }, // ┘ ├─ AND + { // ┐ │ + d: 4 // ├─ Caveat ─────────┘ + } // ┘ ] ``` @@ -496,26 +431,22 @@ Expressing caveats in this standard way simplifies ad hoc extension at delegatio ``` js // Original Caveat [ - [ - { - "uri": "https://blog.example.com/", - "tag": "news" - } - ] + { + "uri": "https://blog.example.com/", + "tag": "news" + } ] // Attenuated Caveat [ - [ - { - "uri": "https://blog.example.com/", - "tag": "news" - }, - // AND - { - "tag": "breaking" - } - ] + { + "uri": "https://blog.example.com/", + "tag": "news" + }, + // AND + { + "tag": "breaking" + } ] ``` @@ -525,16 +456,14 @@ This is also helpful if each object has a special meaning or sense: ``` js [ - [ - { - "type": "path", - "segments": ["blog", "october"] - }, - { - "type": "market", - "segments": ["manufacturing", "healthcare", "service", "technology"] - } - ] + { + "type": "path", + "segments": ["blog", "october"] + }, + { + "type": "market", + "segments": ["manufacturing", "healthcare", "service", "technology"] + } ] ``` @@ -543,90 +472,20 @@ Note that while adding whole objects is useful in many situation as above, atten ``` js // Original Caveat [ - [ - { - "uri": "https://blog.example.com/", - "tag": "news" - } - ] + { + "uri": "https://blog.example.com/", + "tag": "news" + } ] // Attenuated Caveat [ - [ - { - "uri": "https://blog.example.com/", - "tag": "news", - "status": "draft" // New field - } - ] -] -``` - -### 4.4.2.1 Compact Form - -Normal from is consistent, but needlessly verbose for simple cases. Compact form omits superfluous branches. Consider the following normal form capabilities: - -``` json -{ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "ucan/*": [[{}]] - }, - "did:web:example.com": { - "crud/read": [ - [ - { - "uri": "https://blog.example.com", - "status": "published" - } - ] - ], - "crud/update": [ - [ - { - "uri": "https://example.com/newsletter/", - "status": "draft" - } - ], - [ - { - "uri": "https://blog.example.com", - "status": "published", - "tag": "news" - }, - { "tag": "breaking" } - ] - ] + { + "uri": "https://blog.example.com/", + "tag": "news", + "status": "draft" // New field } -} -``` - -The above MAY be expressed in compact form as follows: - -``` json -{ - "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": "ucan/*", - "did:web:example.com": { - "crud/read": { - "uri": "https://blog.example.com", - "status": "published" - }, - "crud/update": [ - { - "uri": "https://example.com/newsletter/", - "status": "draft" - }, - [ - { - "uri": "https://blog.example.com", - "status": "published", - "tag": "news" - }, - { "tag": "breaking" } - ] - ] - } -} +] ``` # 5 Validation @@ -765,37 +624,22 @@ The caveat array SHOULD NOT be empty, as an empty array means "in no case" (whic Here are some abstract cases given in [normal form]. -| Proof Caveats | Delegated Caveats | Is Valid? | Comment | -|------------------------|------------------------------------|-----------|------------------------------------------------| -| `[[{}]]` | `[[{}]]` | Yes | Equal | -| `[[{a: 1}]]` | `[[{a: 1}]]` | Yes | Equal | -| `[[{a: 1}]]` | `[[{}]]` | No | Escalation by removing fields | -| `[[{}]]` | `[[{a: 1}]]` | Yes | Attenuates `{}` by adding fields | -| `[[{a: 1}]]` | `[[{b: 2}]]` | No | Escalation by using a different caveat | -| `[{a: 1}], [{b: 2}]]` | `[[{a: 1}]]` | Yes | Removes a capability (removes an `OR` branch) | -| `[[{a: 1}], [{b: 2}]]` | `[[{a: 1}], [{b: 2, c: 3}]]` | Yes | Attenuates existing caveat | -| `[[{a: 1}]]` | `[[{a: 1}, {a: 2}]]` | Yes | Adds new caveat inside an `AND` block | -| `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]]` | No | Escalation by adding new capability | -| `[[{a: 1}]]` | `[[{a: 1}], [{b: 2}]]` | No | Escalation by adding new capability (`{b: 2}`) | -| `[[{a: 1}]]` | `[[{a: 1, b: 2}], [{a: 1, c: 3}]]` | Yes | Attenuates the original capability | +| Proof Caveats | Delegated Caveats | Is Valid? | Comment | +|------------------------|--------------------|-----------|---------------------------------------| +| `[{}]` | `[{}]` | Yes | Equal | +| `[{a: 1}]` | `[[{a: 1}]]` | Yes | Equal | +| `[{}]` | `[[{a: 1}]]` | Yes | Attenuates `{}` by adding fields | +| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | Yes | Attenuates existing caveat | +| `[{a: 1}]` | `[[{a: 1}, {a: 2}]]` | Yes | Adds new caveat inside an `AND` block | +| `[{a: 1}]` | `[[{}]]` | No | Escalation by removing fields | +| `[{a: 1}]` | `[[{b: 2}]]` | No | Escalation by replacing fields | # 6. Content Identifiers -A UCAN token MUST be referenced as a [base32] [CIDv1]. [SHA2-256] is the RECOMMENDED hash algorithm. - -The [`0x55` raw data][raw data multicodec] codec MUST be supported. If other codecs are used (such as [`0x0129` `dag-json` multicodec][dag-json multicodec]), the UCAN MUST be able to be interpreted as a valid JWT (including the signature). +A UCAN token SHOULD be referenced as a [base32] [CIDv1]. [BLAKE3] is the RECOMMENDED hash algorithm. The [DAG-CBOR] codec MUST be supported, and [DAG-JSON] support is RECOMMENDED. The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. -## 6.1 CID Canonicalization - -A canonical CID can be important for some use cases, such as caching and [revocation]. A canonical CID MUST conform to the following: - -* [CIDv1] -* [base32] -* [SHA2-256] -* [Raw data multicodec] (`0x55`) - # 7. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. From f7c9e79c323de9690133ad5146d7d1a915e2eb77 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 11:55:38 -0700 Subject: [PATCH 054/134] Split out ipldsch --- README.md | 89 +++++++++++++++++----------------------------- delegation.ipldsch | 28 +++++++++++++++ 2 files changed, 60 insertions(+), 57 deletions(-) create mode 100644 delegation.ipldsch diff --git a/README.md b/README.md index d0ad7af..02cea96 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,6 @@ # UCAN Delegation Specification v1.0.0-rc.1 -FIXME IPLD? - -``` -type Signed struct { - del &Delegation - sig Signature -} - -type Delegation struct { - ucd Semver - - iss DID - aud DID - - exp Integer - nbf Integer - - sub DID - aby String - cav [{String : Any}] -} - -type Signature union { - | batch &BatchSig - | inline Bytes -} - -type BatchSig struct { - scp [&Any] - sig Bytes -} - -FIXME if you used a merkle tree, you could embed the merkle proof inside the payload, but then it's really tied down -``` +FIXME move fct to meta for consistency? ## Editors @@ -50,7 +17,7 @@ FIXME if you used a merkle tree, you could embed the merkle proof inside the pay * [UCAN] * [DID] -* [DSG-CBOR] +* [DAG-CBOR] ## Language @@ -77,18 +44,22 @@ The payload MUST describe the authorization claims, who is involved, and its val | Field | Type | Description | Required | |-------|-------------------------------------------|--------------------------------------------------------|----------| -| `ucv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | +| `udv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | | `iss` | `DID` | Issuer DID (sender) | Yes | | `aud` | `DID` | Audience DID (receiver) | Yes | | `nbf` | `Integer` (53-bits[^js-num-size]) | Not Before UTC Unix Timestamp in seconds (valid from) | No | | `exp` | `Integer \| null` (53-bits[^js-num-size]) | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | | `nnc` | `String` | Nonce | Yes | -| `fct` | `{String: Any}` | Facts (asserted, signed data) | No | -| `cap` | `{DID: {Ability: Caveat}}` | Capabilities | Yes | +| `mta` | `{String : Any}` | Facts (asserted, signed data) | No | +| `sub` | `DID` | Principal that the chain is about (subject) | Yes | +| `can` | `String` | [Ability] | Yes | +| `but` | `[Caveat]` | Caveats | Yes | + +The payload MUST be serialized as IPLD and signed over FIXME ## 2.1 Version -The `ucv` field sets the version of the UCAN specification used in the payload. +The `udv` field sets the version of the UCAN Delegation specification used in the payload. ## 2.2 Principals @@ -225,7 +196,7 @@ Here is an illustrative example: // ... sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" can: "msg/send", - cav: [ + but: [ { "sender": "mailto:alice@example.com", "day": "friday" @@ -260,7 +231,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource can: "crud/update", - cav: [{status: "draft"}] + but: [{status: "draft"}] // ... } ``` @@ -271,7 +242,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", can: "crud/create", - cav: [ + but: [ { "uri": "https://example.com/blog/", // Resource "status": "draft" @@ -289,7 +260,7 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { can: "msg/send", - cav: [ + but: [ { "from": "mailto:alice@example.com", "to": "mailto:bob@example.com" @@ -363,7 +334,7 @@ On validation, the caveat array MUST be treated as a logically disjunct (`OR`). { sub: "did:web:example.com", can: "crud/update", - cav: [ + but: [ { // ┐ "uri": "https://blog.example.com", // │ "status": "published" // ├─ Caveat @@ -393,12 +364,12 @@ The "True" caveat MUST represent the lack of caveat. In predicate logic terms, i ``` js { - cav: [{}], + but: [{}], // ... } { - cav: [], + but: [], // ... } ``` @@ -624,23 +595,27 @@ The caveat array SHOULD NOT be empty, as an empty array means "in no case" (whic Here are some abstract cases given in [normal form]. -| Proof Caveats | Delegated Caveats | Is Valid? | Comment | -|------------------------|--------------------|-----------|---------------------------------------| -| `[{}]` | `[{}]` | Yes | Equal | -| `[{a: 1}]` | `[[{a: 1}]]` | Yes | Equal | -| `[{}]` | `[[{a: 1}]]` | Yes | Attenuates `{}` by adding fields | -| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | Yes | Attenuates existing caveat | -| `[{a: 1}]` | `[[{a: 1}, {a: 2}]]` | Yes | Adds new caveat inside an `AND` block | -| `[{a: 1}]` | `[[{}]]` | No | Escalation by removing fields | -| `[{a: 1}]` | `[[{b: 2}]]` | No | Escalation by replacing fields | +| Proof Caveats | Delegated Caveats | Is Valid? | Comment | +|----------------------|--------------------|-----------|---------------------------------------| +| `[{}]` | `[{}]` | Yes | Equal | +| `[{a: 1}]` | `[{a: 1}]` | Yes | Equal | +| `[{}]` | `[{a: 1}]` | Yes | Attenuates `{}` by adding fields | +| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | Yes | Attenuates existing caveat | +| `[{a: 1}]` | `[{a: 1}, {a: 2}]` | Yes | Adds new caveat inside an `AND` block | +| `[{a: 1}]` | `[{}]]` | No | Escalation by removing fields | +| `[{a: 1}]` | `[{b: 2}]` | No | Escalation by replacing fields | + +# 6. Signature + +FIXME -# 6. Content Identifiers +# 7. Content Identifiers A UCAN token SHOULD be referenced as a [base32] [CIDv1]. [BLAKE3] is the RECOMMENDED hash algorithm. The [DAG-CBOR] codec MUST be supported, and [DAG-JSON] support is RECOMMENDED. The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. -# 7. Acknowledgments +# 8. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. diff --git a/delegation.ipldsch b/delegation.ipldsch new file mode 100644 index 0000000..623edf5 --- /dev/null +++ b/delegation.ipldsch @@ -0,0 +1,28 @@ +type Delegation struct { + dgn &DelegationPayload + sig Signature +} + +type DelegationPayload struct { + udv Semver + + iss DID + aud DID + + exp Integer + nbf Integer + + sub DID + can String + but [{String : Any}] +} + +type Signature union { + | batch &BatchSig + | inline Bytes +} + +type BatchSig struct { + scp [&Any] + sig Bytes +} From f196fd53a4cba6116375d902dc63f9026a95a7a4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:07:26 -0700 Subject: [PATCH 055/134] consistent naming --- README.md | 55 +++++++++++++++++++++++----------------------- delegation.ipldsch | 2 +- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 02cea96..cb442bb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # UCAN Delegation Specification v1.0.0-rc.1 -FIXME move fct to meta for consistency? - ## Editors * [Brooklyn Zelenka], [Fission] @@ -38,22 +36,22 @@ Design goals: - Ad hoc caveats - Consistency for interoperability -# 2 Token Structure +# 2 Structure The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Description | Required | -|-------|-------------------------------------------|--------------------------------------------------------|----------| -| `udv` | `String` | UCAN Semantic Version (`1.0.0-rc.1`) | Yes | -| `iss` | `DID` | Issuer DID (sender) | Yes | -| `aud` | `DID` | Audience DID (receiver) | Yes | -| `nbf` | `Integer` (53-bits[^js-num-size]) | Not Before UTC Unix Timestamp in seconds (valid from) | No | -| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Expiration UTC Unix Timestamp in seconds (valid until) | Yes | -| `nnc` | `String` | Nonce | Yes | -| `mta` | `{String : Any}` | Facts (asserted, signed data) | No | -| `sub` | `DID` | Principal that the chain is about (subject) | Yes | -| `can` | `String` | [Ability] | Yes | -| `but` | `[Caveat]` | Caveats | Yes | +| Field | Type | Required | Description | +|-------|-------------------------------------------|----------|-----------------------------------------------------------| +| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | Not Before UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| `nnc` | `String` | Yes | Nonce | +| `mta` | `{String : Any}` | No | Meta (asserted, signed data) — is not delegated authority | +| `sub` | `DID` | Yes | Principal that the chain is about (subject) | +| `can` | `String` | Yes | [Ability] | +| `iff` | `[Caveat]` | Yes | Caveats | The payload MUST be serialized as IPLD and signed over FIXME @@ -140,7 +138,7 @@ The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's crypto suite. -This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`fct`][Facts] field for more. +This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`mta`][Meta] field for more. Here is a simple example. @@ -151,16 +149,16 @@ Here is a simple example. } ``` -## 2.5 Facts +## 2.5 Meta -The OPTIONAL `fct` field contains a map of arbitrary facts and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. +The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. Below is an example: ``` js { // ... - "fct": { + "mta": { "challenges": { "example.com": "abcdef", "another.example.net": "12345" @@ -196,7 +194,7 @@ Here is an illustrative example: // ... sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" can: "msg/send", - but: [ + iff: [ { "sender": "mailto:alice@example.com", "day": "friday" @@ -231,7 +229,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource can: "crud/update", - but: [{status: "draft"}] + iff: [{status: "draft"}] // ... } ``` @@ -242,7 +240,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", can: "crud/create", - but: [ + iff: [ { "uri": "https://example.com/blog/", // Resource "status": "draft" @@ -260,7 +258,7 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti { sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { can: "msg/send", - but: [ + iff: [ { "from": "mailto:alice@example.com", "to": "mailto:bob@example.com" @@ -334,7 +332,7 @@ On validation, the caveat array MUST be treated as a logically disjunct (`OR`). { sub: "did:web:example.com", can: "crud/update", - but: [ + iff: [ { // ┐ "uri": "https://blog.example.com", // │ "status": "published" // ├─ Caveat @@ -364,12 +362,12 @@ The "True" caveat MUST represent the lack of caveat. In predicate logic terms, i ``` js { - but: [{}], + iff: [{}], // ... } { - but: [], + iff: [], // ... } ``` @@ -611,6 +609,8 @@ FIXME # 7. Content Identifiers +FIXME move tro high level spec + A UCAN token SHOULD be referenced as a [base32] [CIDv1]. [BLAKE3] is the RECOMMENDED hash algorithm. The [DAG-CBOR] codec MUST be supported, and [DAG-JSON] support is RECOMMENDED. The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. @@ -644,7 +644,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Caveat]: #44-caveats -[Facts]: #325-facts +[Meta]: #25-meta [Subject]: #41-subject [Wildcard Ability]: #4312--aka-wildcard [compact form]: #4421-compact-form @@ -664,6 +664,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Christopher Joel]: https://github.com/cdata [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ +[DID]: https://www.w3.org/TR/did-core/ [DNF]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [Dan Finlay]: https://github.com/danfinlay [Daniel Holmgren]: https://github.com/dholms diff --git a/delegation.ipldsch b/delegation.ipldsch index 623edf5..4fcf82a 100644 --- a/delegation.ipldsch +++ b/delegation.ipldsch @@ -1,6 +1,6 @@ type Delegation struct { dgn &DelegationPayload - sig Signature + sig &Signature } type DelegationPayload struct { From cc0b75d48ed9d6fc135ee8e72aa7837e53a4b32c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:08:36 -0700 Subject: [PATCH 056/134] Fix broken link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb442bb..7c1a183 100644 --- a/README.md +++ b/README.md @@ -662,6 +662,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata +[DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ [DID]: https://www.w3.org/TR/did-core/ From 5e7af6239229e58a2311ffa3f33ab2c3804f0ada Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:09:23 -0700 Subject: [PATCH 057/134] remove redundant link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c1a183..a5aff7f 100644 --- a/README.md +++ b/README.md @@ -665,7 +665,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ -[DID]: https://www.w3.org/TR/did-core/ [DNF]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [Dan Finlay]: https://github.com/danfinlay [Daniel Holmgren]: https://github.com/dholms @@ -713,3 +712,4 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems [revocation]: https://github.com/ucan-wg/revocation [ucan.xyz]: https://ucan.xyz + From 24d43293a86b50481dcddc59b8de3a7ce00b5163 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:10:42 -0700 Subject: [PATCH 058/134] Missing internal link --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a5aff7f..f8d04c7 100644 --- a/README.md +++ b/README.md @@ -643,13 +643,14 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[Ability]: #33-abilities [Caveat]: #44-caveats [Meta]: #25-meta [Subject]: #41-subject [Wildcard Ability]: #4312--aka-wildcard [compact form]: #4421-compact-form [normal form]: #4421-normal-form - + [Alan Karp]: https://github.com/alanhkarp From dee748a51566ed45d0f695609c1ce1fb169528cc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:13:58 -0700 Subject: [PATCH 059/134] consisteny --- README.md | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f8d04c7..17d322f 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,20 @@ ## Editors -* [Brooklyn Zelenka], [Fission] +- [Brooklyn Zelenka], [Fission] ## Authors -* [Brooklyn Zelenka], [Fission] -* [Daniel Holmgren], [Bluesky] -* [Irakli Gozalishvili], [Protocol Labs] -* [Philipp Krüger], [Fission] +- [Brooklyn Zelenka], [Fission] +- [Daniel Holmgren], [Bluesky] +- [Irakli Gozalishvili], [Protocol Labs] +- [Philipp Krüger], [Fission] ## Dependencies -* [UCAN] -* [DID] -* [DAG-CBOR] +- [UCAN] +- [DID] +- [DAG-CBOR] ## Language @@ -103,7 +103,7 @@ The `exp` field MUST be set. Following the [principle of least authority][PoLA], Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. -Several named points of time in the UCAN lifecycle [can be found in the high level spec]. +Several named points of time in the UCAN lifecycle can be found in the [high level spec][UCAN]. [^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. @@ -607,15 +607,7 @@ Here are some abstract cases given in [normal form]. FIXME -# 7. Content Identifiers - -FIXME move tro high level spec - -A UCAN token SHOULD be referenced as a [base32] [CIDv1]. [BLAKE3] is the RECOMMENDED hash algorithm. The [DAG-CBOR] codec MUST be supported, and [DAG-JSON] support is RECOMMENDED. - -The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service. - -# 8. Acknowledgments +# 7. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. From a6bd120cf0c6318afa929144ac5651a48f9a573a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:19:43 -0700 Subject: [PATCH 060/134] Be clear about signing --- README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 17d322f..60e903a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Design goals: - Ad hoc caveats - Consistency for interoperability -# 2 Structure +# 2 Delegation Payload The payload MUST describe the authorization claims, who is involved, and its validity period. @@ -53,7 +53,7 @@ The payload MUST describe the authorization claims, who is involved, and its val | `can` | `String` | Yes | [Ability] | | `iff` | `[Caveat]` | Yes | Caveats | -The payload MUST be serialized as IPLD and signed over FIXME +The payload MUST be serialized as IPLD and [signed over][Signature]. ## 2.1 Version @@ -457,7 +457,14 @@ Note that while adding whole objects is useful in many situation as above, atten ] ``` -# 5 Validation +# 5. Signature + +| Field | Type | Required | Description | +|---------|-------------|----------|----------------------------------------------| +| `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | +| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | + +# 6 Validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. @@ -465,7 +472,7 @@ Each capability has its own semantics, which needs to be interpretable by the [E If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: -## 5.1 Time Bounds +## 6.1 Time Bounds A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." @@ -506,7 +513,7 @@ const ensureProofExp = (ucan, proof) => { } ``` -## 5.2 Principal Alignment +## 6.2 Principal Alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. @@ -571,7 +578,7 @@ In the above diagram, Alice has some storage. This storage may exist in one loca Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the invoking compute service. The invoking service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. -### 5.2.1 Recipient Validation +### 6.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. @@ -587,7 +594,7 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -## 5.4 Caveat Attenuation +## 6.4 Caveat Attenuation The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. @@ -603,9 +610,9 @@ Here are some abstract cases given in [normal form]. | `[{a: 1}]` | `[{}]]` | No | Escalation by removing fields | | `[{a: 1}]` | `[{b: 2}]` | No | Escalation by replacing fields | -# 6. Signature +## 6.5 Signature Validation -FIXME +The `sig` field MUST validate against the `iss` DID from the [Payload]. # 7. Acknowledgments From df13bc17e087a574fa49478b3a2f409ee80ad6eb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:22:40 -0700 Subject: [PATCH 061/134] Fix more internal links --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 60e903a..ee20c46 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The payload MUST describe the authorization claims, who is involved, and its val | `can` | `String` | Yes | [Ability] | | `iff` | `[Caveat]` | Yes | Caveats | -The payload MUST be serialized as IPLD and [signed over][Signature]. +The payload MUST be serialized as IPLD and [signed over][Envelope]. ## 2.1 Version @@ -105,8 +105,6 @@ Keeping the window of validity as short as possible is RECOMMENDED. Limiting the Several named points of time in the UCAN lifecycle can be found in the [high level spec][UCAN]. -[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. - Below are a couple examples: ```js @@ -457,7 +455,7 @@ Note that while adding whole objects is useful in many situation as above, atten ] ``` -# 5. Signature +# 5. Delegation (Envelope) | Field | Type | Required | Description | |---------|-------------|----------|----------------------------------------------| @@ -640,11 +638,17 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. + + +[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. + [Ability]: #33-abilities [Caveat]: #44-caveats +[Envelope]: #5-delegation-envelope [Meta]: #25-meta +[Payload]: #2-delegation-payload [Subject]: #41-subject [Wildcard Ability]: #4312--aka-wildcard [compact form]: #4421-compact-form From 2805c6b33a8053fb02c126e30aa2f1fee5d318fc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 21:48:31 -0700 Subject: [PATCH 062/134] Add to dictionary --- .custom-words.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.custom-words.txt b/.custom-words.txt index 9e37797..b8d6bd4 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -1,4 +1,7 @@ ACL +CBOR +IPLD +iff ACLs AlicePhone AliceRoot From b982637be25e77749426c1bcb25ee789a9888e29 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 22:54:55 -0700 Subject: [PATCH 063/134] NOttaional consistency --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index ee20c46..87b1313 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,9 @@ Here is an illustrative example: ``` js { // ... - sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" - can: "msg/send", - iff: [ + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" + "can": "msg/send", + "iff": [ { "sender": "mailto:alice@example.com", "day": "friday" @@ -212,7 +212,7 @@ For example: ``` js { - sub: "did:web:example.com", + "sub": "did:web:example.com", // ... } ``` @@ -225,9 +225,9 @@ By default, the Resource of a capability is the Subject. This makes the delegati ``` js { - sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource - can: "crud/update", - iff: [{status: "draft"}] + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource + "can": "crud/update", + "iff": [{status: "draft"}] // ... } ``` @@ -236,9 +236,9 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` js { - sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", - can: "crud/create", - iff: [ + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", + "can": "crud/create", + "iff": [ { "uri": "https://example.com/blog/", // Resource "status": "draft" @@ -254,9 +254,9 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti ``` js { - sub: "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - can: "msg/send", - iff: [ + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { + "can": "msg/send", + "iff": [ { "from": "mailto:alice@example.com", "to": "mailto:bob@example.com" @@ -284,7 +284,7 @@ The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be tho ``` js { - can: "*", + "can": "*", // ... } ``` @@ -328,9 +328,9 @@ On validation, the caveat array MUST be treated as a logically disjunct (`OR`). ``` js { - sub: "did:web:example.com", - can: "crud/update", - iff: [ + "sub": "did:web:example.com", + "can": "crud/update", + "iff": [ { // ┐ "uri": "https://blog.example.com", // │ "status": "published" // ├─ Caveat @@ -360,12 +360,12 @@ The "True" caveat MUST represent the lack of caveat. In predicate logic terms, i ``` js { - iff: [{}], + "iff": [{}], // ... } { - iff: [], + "iff": [], // ... } ``` From bf064ad4ca267145f3aa1a79a608ac3b577509d1 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 15 Oct 2023 23:27:12 -0700 Subject: [PATCH 064/134] Some links --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 87b1313..6ee0d7c 100644 --- a/README.md +++ b/README.md @@ -40,18 +40,18 @@ Design goals: The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|-------|-------------------------------------------|----------|-----------------------------------------------------------| -| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | Not Before UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `nnc` | `String` | Yes | Nonce | -| `mta` | `{String : Any}` | No | Meta (asserted, signed data) — is not delegated authority | -| `sub` | `DID` | Yes | Principal that the chain is about (subject) | -| `can` | `String` | Yes | [Ability] | -| `iff` | `[Caveat]` | Yes | Caveats | +| Field | Type | Required | Description | +|-------|-------------------------------------------|----------|-------------------------------------------------------------| +| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | Not Before UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| `nnc` | `String` | Yes | Nonce | +| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `can` | `String` | Yes | [Ability] | +| `iff` | `[Caveat]` | Yes | [Caveat]s | The payload MUST be serialized as IPLD and [signed over][Envelope]. From 5ad98feabc79fce9c1e83bae1e94a761fb4d0c00 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 12:53:47 -0700 Subject: [PATCH 065/134] Fix pseudocode --- README.md | 49 +++++++++++++--------------------------------- delegation.ipldsch | 8 ++++---- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 6ee0d7c..95aab1e 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,10 @@ The payload MUST describe the authorization claims, who is involved, and its val | `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | | `iss` | `DID` | Yes | Issuer DID (sender) | | `aud` | `DID` | Yes | Audience DID (receiver) | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | Not Before UTC Unix Timestamp in seconds (valid from) | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | | `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | | `nnc` | `String` | Yes | Nonce | -| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | | `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | | `can` | `String` | Yes | [Ability] | | `iff` | `[Caveat]` | Yes | [Caveat]s | @@ -348,7 +348,7 @@ The above MUST be interpreted as the set of capabilities below in the following | Subject | Ability | Caveat | |-----------------------|---------------|--------------------------------------------------------------------------------------------------------| -| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `published` and `news` | +| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `news` and `breaking` | When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. @@ -376,7 +376,7 @@ Caveats MAY be expressed in a compact form, but any caveat MUST be expressible i Normal form MUST take the following shape: `[{}]`. The arrays represents logical `all` (chained `AND`s). To represent logical `any` / `OR`, issue another delegation with that attenuation. -For instance, the following represents `({a: 1, b:2} AND {c: 3}) AND {d: 4}`: +For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: ``` js [ @@ -472,42 +472,21 @@ If _any_ of the following criteria are not met, the UCAN MUST be considered inva ## 6.1 Time Bounds -A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called "ambient time validity." - -All proofs MUST contain time bounds equal to or broader than the UCAN being delegated (i.e. delegation maintains or narrows the time bounds). If the proof expires before the delegated UCAN — or starts after it — the validator MUST treat the entire UCAN as invalid. Delegation inside of the time bound is called "timely delegation." These conditions MUST hold even if the current wall clock time is inside of incorrectly delegated bounds. - -A UCAN is valid inclusive from the `nbf` time and until the `exp` field. If the current time is outside of these bounds, the UCAN MUST be considered invalid. When setting these bounds, a delegator or invoker SHOULD account for expected clock drift. Use of time bounds this way is called "timely invocation." +A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegtaion chain valid between the latest `nbf` and earliest `exp`. ``` js // Pseudocode -const ensureTime = async (ucan) => { - ensureTimeBounds(ucan) - await Promise.all(ucan.prf.forEach(async (cid) => { - const proof = await getUcan(cid) - ensureProofNbf(ucan, proof) - ensureProofExp(ucan, proof) - })) -} - -// Helpers - -const ensureTimeBounds = (ucan) => { - if (!!proof.nbf && ucan.exp !== null && ucan.nbf > ucan.exp) { - throw new Error("Time violation: UCAN starts after expiry") - } -} - -const ensureProofNbf = (ucan, proof) => { - if (!!proof.nbf && ucan.nbf < proof.nbf) { - throw new Error("Time escalation: delegation starts before proof starts") - } -} +const ensureTime = async (delegationChain, now) => { + await Promise.all(delegationChain.forEach(async (cid) => { + if (!!proof.nbf && ucan.nbf < now) { + throw new Error("Time escalation: delegation starts before proof starts") + } -const ensureProofExp = (ucan, proof) => { - if (proof.exp !== null && ucan.exp > proof.exp) { - throw new Error("Time escalation: delegation ends after proof ends") - } + if (proof.exp !== null && ucan.exp > now) { + throw new Error("Time escalation: delegation ends after proof ends") + } + })) } ``` diff --git a/delegation.ipldsch b/delegation.ipldsch index 4fcf82a..b303c72 100644 --- a/delegation.ipldsch +++ b/delegation.ipldsch @@ -14,15 +14,15 @@ type DelegationPayload struct { sub DID can String - but [{String : Any}] + iff [{String : Any}] } type Signature union { - | batch &BatchSig + | batch BatchSig | inline Bytes -} +} representation kinded type BatchSig struct { - scp [&Any] + scp &[Any] sig Bytes } From 9280013aeba0e91158e3b25ed4b1c039c12dc9fd Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 12:55:09 -0700 Subject: [PATCH 066/134] Simplify --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 95aab1e..bf2559f 100644 --- a/README.md +++ b/README.md @@ -477,8 +477,8 @@ A UCAN's time bounds MUST NOT be considered valid if the current system time is ``` js // Pseudocode -const ensureTime = async (delegationChain, now) => { - await Promise.all(delegationChain.forEach(async (cid) => { +const ensureTime = (delegationChain, now) => { + delegationChain.forEach((cid) => { if (!!proof.nbf && ucan.nbf < now) { throw new Error("Time escalation: delegation starts before proof starts") } @@ -486,7 +486,7 @@ const ensureTime = async (delegationChain, now) => { if (proof.exp !== null && ucan.exp > now) { throw new Error("Time escalation: delegation ends after proof ends") } - })) + }) } ``` From 6442db9a50be9edfad3cdc79865835f5da6aa1de Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 15:53:14 -0700 Subject: [PATCH 067/134] Fix intro --- README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index bf2559f..6612296 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,25 @@ ## Dependencies -- [UCAN] - [DID] -- [DAG-CBOR] +- [IPLD] +- [UCAN] ## Language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. -# 0 Abstract +# 0. Abstract -UCAN Delegation is a component of [UCAN]. This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible caveats. +This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible caveats. -# 1 Introduction +# 1. Introduction -## 1.1 Motivation +UCAN Delegation is a certificate capability system with runtime-extensiblity, ad hoc caveats, cachability, and focused on ease of use and interoperabilty. Delegations act as a proofs for [UCAN Invocation]s. -Design goals: +Delegation provides a way to "transfer authority without transferring cryptograhic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. -- Flexible delegation paths -- Semantic extensibility -- Ad hoc caveats -- Consistency for interoperability - -# 2 Delegation Payload +# 2. Delegation Payload The payload MUST describe the authorization claims, who is involved, and its validity period. @@ -53,7 +48,7 @@ The payload MUST describe the authorization claims, who is involved, and its val | `can` | `String` | Yes | [Ability] | | `iff` | `[Caveat]` | Yes | [Caveat]s | -The payload MUST be serialized as IPLD and [signed over][Envelope]. +The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. ## 2.1 Version @@ -657,6 +652,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Fission]: https://fission.codes [Hugo Dias]: https://github.com/hugomrdias [IEEE-754]: https://ieeexplore.ieee.org/document/8766229 +[IPLD]: https://ipld.io/ [Ink & Switch]: https://www.inkandswitch.com/ [Invocation]: https://github.com/ucan-wg/invocation [Irakli Gozalishvili]: https://github.com/Gozala @@ -695,4 +691,3 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems [revocation]: https://github.com/ucan-wg/revocation [ucan.xyz]: https://ucan.xyz - From cc66477f6ce4f78036b427807a10b221de1a2382 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 15:54:49 -0700 Subject: [PATCH 068/134] Remove JWT reference --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6612296..227fe4a 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Below are a couple examples: ## 2.3 Time Bounds -`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 MUST represent seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. +`nbf` and `exp` stand for "not before" and "expires at," respectively. These MUST be expressed as seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. @@ -657,7 +657,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Invocation]: https://github.com/ucan-wg/invocation [Irakli Gozalishvili]: https://github.com/Gozala [JS Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number -[JWT]: https://www.rfc-editor.org/rfc/rfc7519 [Juan Caballero]: https://github.com/bumblefudge [Mark Miller]: https://github.com/erights [Martin Kleppmann]: https://martin.kleppmann.com/ From 26f119ef8caa432c246c388c0710288c49101a82 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 15:56:27 -0700 Subject: [PATCH 069/134] Remove redundant text --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 227fe4a..b137fef 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ flowchart BT Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. -Caveats MAY be open ended. Caveats MUST be understood by the executor of the eventual [invocation][UCAN Invocation]. Caveats MUST be formatted as maps. +Caveats MAY be open ended. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Caveats MUST be formatted as maps. On validation, the caveat array MUST be treated as a logically disjunct (`OR`). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: @@ -347,8 +347,6 @@ The above MUST be interpreted as the set of capabilities below in the following When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. -Note that all caveats need to be understood by the Executor. - ### 4.4.1 The "True" Caveat The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[{}]`, but is equivalent to `[]`. From a993da2662cbb043be6e8b6e7fedcb713ce6e524 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 15:58:42 -0700 Subject: [PATCH 070/134] Fix plurality --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b137fef..2bec508 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Below is an example: } ``` -# 3. Capabilities +# 3. Capability Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: @@ -243,7 +243,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS } ``` -## 3.3 Abilities +## 3.3 Ability Abilities MUST be presented as a case-insensitive string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. @@ -616,7 +616,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Ability]: #33-abilities +[Ability]: #33-ability [Caveat]: #44-caveats [Envelope]: #5-delegation-envelope [Meta]: #25-meta From ec777298872f90accf87465302edd574ea8eb9d4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:05:21 -0700 Subject: [PATCH 071/134] Add clarifying text about signatures --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2bec508..c78345a 100644 --- a/README.md +++ b/README.md @@ -450,6 +450,8 @@ Note that while adding whole objects is useful in many situation as above, atten # 5. Delegation (Envelope) +Delegations MUST include a signature that validates against the `iss` DID. A Delegation Payload on its own MUST NOT be considered a valid Delegation. + | Field | Type | Required | Description | |---------|-------------|----------|----------------------------------------------| | `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | From 2add9c20d32c55f021bb7734eda6466723b83998 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:09:02 -0700 Subject: [PATCH 072/134] Fix numbering and typos --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c78345a..01d70ae 100644 --- a/README.md +++ b/README.md @@ -473,13 +473,13 @@ A UCAN's time bounds MUST NOT be considered valid if the current system time is // Pseudocode const ensureTime = (delegationChain, now) => { - delegationChain.forEach((cid) => { - if (!!proof.nbf && ucan.nbf < now) { - throw new Error("Time escalation: delegation starts before proof starts") + delegationChain.forEach((ucan) => { + if (!!ucan.nbf && now < can.nbf) { + throw new Error(`Delegation is not yet valid, but will become valid at ${ucan.nbf}`) } - if (proof.exp !== null && ucan.exp > now) { - throw new Error("Time escalation: delegation ends after proof ends") + if (ucan.exp !== null && now > ucan.exp) { + throw new Error(`Delegation expired at ${ucan.exp}`) } }) } @@ -566,7 +566,7 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -## 6.4 Caveat Attenuation +## 6.3 Caveat Attenuation The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. @@ -582,7 +582,7 @@ Here are some abstract cases given in [normal form]. | `[{a: 1}]` | `[{}]]` | No | Escalation by removing fields | | `[{a: 1}]` | `[{b: 2}]` | No | Escalation by replacing fields | -## 6.5 Signature Validation +## 6.4 Signature Validation The `sig` field MUST validate against the `iss` DID from the [Payload]. From 4d0a00719c00b1df628e2b2b43724e83396c9775 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:22:48 -0700 Subject: [PATCH 073/134] Remove anything about DNF --- README.md | 126 +++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 01d70ae..966d5f3 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,24 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 0. Abstract This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible caveats. - + # 1. Introduction UCAN Delegation is a certificate capability system with runtime-extensiblity, ad hoc caveats, cachability, and focused on ease of use and interoperabilty. Delegations act as a proofs for [UCAN Invocation]s. Delegation provides a way to "transfer authority without transferring cryptograhic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. -# 2. Delegation Payload +# 2. Delegation (Envelope) + +Delegations MUST include a signature that validates against the `iss` DID. A Delegation Payload on its own MUST NOT be considered a valid Delegation. + +| Field | Type | Required | Description | +|---------|-------------|----------|----------------------------------------------| +| `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | +| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | + + +# 3. Delegation Payload The payload MUST describe the authorization claims, who is involved, and its validity period. @@ -50,11 +60,11 @@ The payload MUST describe the authorization claims, who is involved, and its val The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. -## 2.1 Version +## 3.1 Version The `udv` field sets the version of the UCAN Delegation specification used in the payload. -## 2.2 Principals +## 3.2 Principals The `iss` and `aud` fields describe the token's principals. They are distinguished by having DIDs. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. @@ -88,7 +98,7 @@ Below are a couple examples: "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", ``` -## 2.3 Time Bounds +## 3.3 Time Bounds `nbf` and `exp` stand for "not before" and "expires at," respectively. These MUST be expressed as seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. @@ -125,7 +135,7 @@ Below are a couple examples: } ``` -## 2.4 Nonce +## 3.4 Nonce The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. @@ -142,7 +152,7 @@ Here is a simple example. } ``` -## 2.5 Meta +## 3.5 Meta The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. @@ -163,7 +173,7 @@ Below is an example: } ``` -# 3. Capability +# 4. Capability Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: @@ -199,7 +209,7 @@ Here is an illustrative example: } ``` -## 3.1 Subject +## 4.1 Subject The Subject MUST be the DID that initiated the delegation chain. @@ -212,7 +222,7 @@ For example: } ``` -## 3.2 Resource +## 4.2 Resource Unlike Subjects and Abilities, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. @@ -243,7 +253,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS } ``` -## 3.3 Ability +## 4.3 Ability Abilities MUST be presented as a case-insensitive string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. @@ -263,15 +273,15 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. -### 3.3.1 Reserved Abilities +### 4.3.1 Reserved Abilities -#### 3.3.1.1 `ucan` Namespace +#### 4.3.1.1 `ucan` Namespace The `ucan` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` Support for the `ucan/*` delegated proof ability is RECOMMENDED. -#### 3.3.1.2 `*` AKA "Wildcard" +#### 4.3.1.2 `*` AKA "Wildcard" _"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ @@ -313,13 +323,9 @@ flowchart BT ... --> * ``` -## 3.4 Caveats - -Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. - -Caveats MAY be open ended. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Caveats MUST be formatted as maps. +## 4.4 Caveats -On validation, the caveat array MUST be treated as a logically disjunct (`OR`). In other words: passing validation against _any_ caveat in the array MUST pass the check. For example, consider the following capabilities: +Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each caveats MUST be formatted as a map. ``` js { @@ -345,31 +351,7 @@ The above MUST be interpreted as the set of capabilities below in the following |-----------------------|---------------|--------------------------------------------------------------------------------------------------------| | `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `news` and `breaking` | -When validating a delegation chain in the abstract, all caveats MUST be present in each successive delegation. At invocation time, only the capability being invoked MUST be match the delegation chain. - -### 4.4.1 The "True" Caveat - -The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. In [normal form], it MUST be represented as `[{}]`, but is equivalent to `[]`. - -``` js -{ - "iff": [{}], - // ... -} - -{ - "iff": [], - // ... -} -``` - -### 4.4.2.1 Normal Form - -Caveats MAY be expressed in a compact form, but any caveat MUST be expressible in disjunctive normal form ([DNF]). Expanding to normal form during validation is RECOMMENDED to ease checking. - -Normal form MUST take the following shape: `[{}]`. The arrays represents logical `all` (chained `AND`s). To represent logical `any` / `OR`, issue another delegation with that attenuation. - -For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: +The `iff` caveat set MUST take the following shape: `[{}]`. The arrays represents logical `all` (chained `AND`s). To represent logical `any` / `OR`, issue another delegation with that attenuation. For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: ``` js [ @@ -386,7 +368,7 @@ For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: ] ``` -Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, if a root UCAN caveat has a `tag` field but no `tags` field, it is still possible to express multiplicity by adding another `AND`ed caveat: +Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, if a root UCAN caveat has a `tag` field but no (plural) `tags` field, it is still possible to express multiplicity by adding another `AND`ed caveat: ``` js // Original Caveat @@ -427,7 +409,7 @@ This is also helpful if each object has a special meaning or sense: ] ``` -Note that while adding whole objects is useful in many situation as above, attenuation MAY also be achieved by adding fields to an object: +Note that while adding whole objects is useful in many situations (as above), attenuation MAY also be achieved by adding fields to an object: ``` js // Original Caveat @@ -448,16 +430,23 @@ Note that while adding whole objects is useful in many situation as above, atten ] ``` -# 5. Delegation (Envelope) +### 4.4.1 The "True" Caveat -Delegations MUST include a signature that validates against the `iss` DID. A Delegation Payload on its own MUST NOT be considered a valid Delegation. +The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. It SHOULD be represented as `[{}]`, but `[]` MAY be used equivalently ("without any caveats"). -| Field | Type | Required | Description | -|---------|-------------|----------|----------------------------------------------| -| `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | -| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | +``` js +{ + "iff": [{}], + // ... +} + +{ + "iff": [], + // ... +} +``` -# 6 Validation +# 5 Validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. @@ -465,7 +454,7 @@ Each capability has its own semantics, which needs to be interpretable by the [E If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: -## 6.1 Time Bounds +## 5.1 Time Bounds A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegtaion chain valid between the latest `nbf` and earliest `exp`. @@ -485,7 +474,7 @@ const ensureTime = (delegationChain, now) => { } ``` -## 6.2 Principal Alignment +## 5.2 Principal Alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. @@ -550,7 +539,7 @@ In the above diagram, Alice has some storage. This storage may exist in one loca Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the invoking compute service. The invoking service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. -### 6.2.1 Recipient Validation +### 5.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. @@ -566,14 +555,17 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -## 6.3 Caveat Attenuation +## 5.3 Caveat Attenuation The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. -Here are some abstract cases given in [normal form]. +Here are some abstract examples: | Proof Caveats | Delegated Caveats | Is Valid? | Comment | |----------------------|--------------------|-----------|---------------------------------------| +| `[]` | `[]` | Yes | Equal | +| `[]` | `[{}]` | Yes | [True Caveat] | +| `[{}]` | `[]` | Yes | [True Caveat] | | `[{}]` | `[{}]` | Yes | Equal | | `[{a: 1}]` | `[{a: 1}]` | Yes | Equal | | `[{}]` | `[{a: 1}]` | Yes | Attenuates `{}` by adding fields | @@ -582,11 +574,11 @@ Here are some abstract cases given in [normal form]. | `[{a: 1}]` | `[{}]]` | No | Escalation by removing fields | | `[{a: 1}]` | `[{b: 2}]` | No | Escalation by replacing fields | -## 6.4 Signature Validation +## 5.4 Signature Validation The `sig` field MUST validate against the `iss` DID from the [Payload]. -# 7. Acknowledgments +# 6. Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. @@ -618,15 +610,14 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Ability]: #33-ability +[Ability]: #43-ability [Caveat]: #44-caveats -[Envelope]: #5-delegation-envelope -[Meta]: #25-meta -[Payload]: #2-delegation-payload +[Envelope]: #2-delegation-envelope +[Meta]: #35-meta +[Payload]: #3-delegation-payload [Subject]: #41-subject +[True Caveat]: #441-the-true-caveat [Wildcard Ability]: #4312--aka-wildcard -[compact form]: #4421-compact-form -[normal form]: #4421-normal-form @@ -643,7 +634,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ -[DNF]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [Dan Finlay]: https://github.com/danfinlay [Daniel Holmgren]: https://github.com/dholms [ES256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.4 From 5384435922b52d599c510520ffe1be08ccce241f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:27:32 -0700 Subject: [PATCH 074/134] Update terminology --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 966d5f3..8e338ca 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,9 @@ Here is a simple example. ## 3.5 Meta -The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. Facts themselves MUST NOT be semantically meaningful to delegation chains. +The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. + +The data contained in this map MUST NOT be semantically meaningful to delegation chains. Below is an example: From 61e698db1cccba11bfcaf977552070388af24ff0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:29:01 -0700 Subject: [PATCH 075/134] More of same --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e338ca..1894ae4 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Operation]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +Abilities MAY be organized in a hierarchy that abstract over many [Action]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. ### 4.3.1 Reserved Abilities From 62d490367d48889552f85b794598db8438b6b64b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:32:37 -0700 Subject: [PATCH 076/134] Fix name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1894ae4..93cebb2 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ Abilities MUST be presented as a case-insensitive string. By convention, abiliti } ``` -Abilities MAY be organized in a hierarchy that abstract over many [Action]s. A typical example is a superuser capability ("anything") on a file system. Another is command vs query access, such that in an HTTP context, `WRITE` implies `PUT`, `PATCH`, `DELETE`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A typical example is a superuser capability ("anything") on a file system. Another is mutation vs read access, such that in an HTTP context, `write` implies `put`, `patch`, `delete`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. ### 4.3.1 Reserved Abilities @@ -655,7 +655,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Mikael Rogers]: https://github.com/mikeal/ [OCAP]: https://en.wikipedia.org/wiki/Object-capability_model [OCapN]: https://github.com/ocapn/ -[Operation]: https://github.com/ucan-wg/spec#33-operation +[Command]: https://github.com/ucan-wg/spec#33-command [Philipp Krüger]: https://github.com/matheus23 [PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [Protocol Labs]: https://protocol.ai/ From 7927523299bae19b78fc2c7dabac3d1bacd067e7 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:40:41 -0700 Subject: [PATCH 077/134] Fix dictionary & spelling --- .custom-words.txt | 8 +++++--- README.md | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.custom-words.txt b/.custom-words.txt index b8d6bd4..bea225f 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -1,7 +1,4 @@ ACL -CBOR -IPLD -iff ACLs AlicePhone AliceRoot @@ -14,6 +11,7 @@ BCP BT Bluesky BxZ +CBOR CIDs CIDv CNAME @@ -42,6 +40,7 @@ HEHYSF HMACs Haus Holmgren +IPLD Invoker's Irakli JSON @@ -103,6 +102,7 @@ autonumber baz bene blockchain +cachability cid codec codecs @@ -115,6 +115,7 @@ crypto cryptographically del delegable +delegationChain delegator dereference disambiguates @@ -142,6 +143,7 @@ getUcan hawaii hoc https +iff init inlining interconnectivity diff --git a/README.md b/README.md index 93cebb2..c00943a 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ This specification describes the semantics and serialization format for [UCAN] d # 1. Introduction -UCAN Delegation is a certificate capability system with runtime-extensiblity, ad hoc caveats, cachability, and focused on ease of use and interoperabilty. Delegations act as a proofs for [UCAN Invocation]s. +UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc caveats, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. -Delegation provides a way to "transfer authority without transferring cryptograhic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. +Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. # 2. Delegation (Envelope) @@ -458,7 +458,7 @@ If _any_ of the following criteria are not met, the UCAN MUST be considered inva ## 5.1 Time Bounds -A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegtaion chain valid between the latest `nbf` and earliest `exp`. +A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegation chain valid between the latest `nbf` and earliest `exp`. ``` js // Pseudocode From 3ecbdd72b691e4ff53e56b40c9702ab45d0ab56c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 16 Oct 2023 16:41:44 -0700 Subject: [PATCH 078/134] Dictionary --- .custom-words.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.custom-words.txt b/.custom-words.txt index bea225f..da3fcff 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -103,6 +103,7 @@ baz bene blockchain cachability +cacheability cid codec codecs @@ -112,6 +113,7 @@ const const crudGraph crypto +cryptographic cryptographically del delegable From 33424030b6b1e625d405eef5513d71513cddd084 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 18 Oct 2023 14:07:28 -0700 Subject: [PATCH 079/134] Fix inconsistency between specs --- README.md | 84 +++++++++++++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index c00943a..0f9edc5 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ Delegations MUST include a signature that validates against the `iss` DID. A Del | `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | | `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | - # 3. Delegation Payload The payload MUST describe the authorization claims, who is involved, and its validity period. @@ -52,11 +51,12 @@ The payload MUST describe the authorization claims, who is involved, and its val | `aud` | `DID` | Yes | Audience DID (receiver) | | `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | | `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `nnc` | `String` | Yes | Nonce | +| `nnc` | `Bytes` | Yes | Nonce | | `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | | `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `can` | `String` | Yes | [Ability] | -| `iff` | `[Caveat]` | Yes | [Caveat]s | +| `cmd` | `String` | Yes | The [Command] to eventually invoke | +| `arg` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | +| `iff` | `[Caveat]` | Yes | Any additional [Caveat]s | The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. @@ -148,7 +148,7 @@ Here is a simple example. ``` js { // ... - "nnc": "_NCC-1701-D_" + "nnc": {"/": {"bytes": "bGlnaHQgd29yay4"}} } ``` @@ -179,18 +179,12 @@ Below is an example: Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -``` -{ $SUBJECT: { $ABILITY: [$CAVEAT] } } -``` - -This map MUST contain some or none of the following: - -0. Nothing -1. Capabilities unchanged from a previous delegation -2. A strict subset (attenuation) of the capability authority from the next direct `prf` field -3. Capabilities about a Subject that matches the Issuer (`iss`) DID - -The anatomy of a capability MUST be given as a mapping of resource URI to abilities to array of caveats. +| Field | Type | Required | Description | +|-------|--------------------|----------|------------------------------------------------------------------------------------------------------| +| `sub` | `URI` | Yes | The [Subject] that this Capability is about | +| `cmd` | `Command` | Yes | The [Command] of this Capability | +| `arg` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | +| `iff` | `[{String : Any}]` | Yes | Any additional caveats, such as regex matchers, contextual information (e.g. day of week), and so on | Here is an illustrative example: @@ -198,16 +192,12 @@ Here is an illustrative example: { // ... "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" - "can": "msg/send", - "iff": [ - { - "sender": "mailto:alice@example.com", - "day": "friday" - }, - { - "subject": "Weekly Reports", - } - ] + "cmd": "msg/send", + "arg": { + "sender": "mailto:alice@example.com", + "subject": "Weekly Reports", + }, + "iff": [{"day": "friday"}] } ``` @@ -226,15 +216,15 @@ For example: ## 4.2 Resource -Unlike Subjects and Abilities, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. +Unlike Subjects and Commands, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. By default, the Resource of a capability is the Subject. This makes the delegation chain self-certifying. ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource - "can": "crud/update", - "iff": [{status: "draft"}] + "cmd": "crud/update", + "arg": {"status": "draft"} // ... } ``` @@ -244,38 +234,34 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", - "can": "crud/create", - "iff": [ - { - "uri": "https://example.com/blog/", // Resource - "status": "draft" - } - ], + "cmd": "crud/create", + "arg":{ + "uri": "https://example.com/blog/", // Resource + "status": "draft" + }, // ... } ``` -## 4.3 Ability +## 4.3 Command -Abilities MUST be presented as a case-insensitive string. By convention, abilities SHOULD be namespaced with a slash, such as `msg/send`. One or more abilities MUST be given for each resource. +Commands MUST be presented as a case-insensitive string. By convention, Commands SHOULD be namespaced with a slash, such as `msg/send`. ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "can": "msg/send", - "iff": [ - { - "from": "mailto:alice@example.com", - "to": "mailto:bob@example.com" - } - ], + "cmd": "msg/send", + "arg": { + "from": "mailto:alice@example.com", + "to": "mailto:bob@example.com" + }, // ... } ``` Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A typical example is a superuser capability ("anything") on a file system. Another is mutation vs read access, such that in an HTTP context, `write` implies `put`, `patch`, `delete`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. -### 4.3.1 Reserved Abilities +### 4.3.1 Reserved Commands #### 4.3.1.1 `ucan` Namespace @@ -291,7 +277,7 @@ The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be tho ``` js { - "can": "*", + "cmd": "*", // ... } ``` @@ -332,7 +318,7 @@ Caveats define a set of constraints on what can be re-delegated or invoked. Cave ``` js { "sub": "did:web:example.com", - "can": "crud/update", + "cmd": "crud/update", "iff": [ { // ┐ "uri": "https://blog.example.com", // │ From ecea7703da865343162f119d858375c1125b6f55 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 19 Oct 2023 18:15:48 -0700 Subject: [PATCH 080/134] Consistency! --- README.md | 282 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 172 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 0f9edc5..70fda2a 100644 --- a/README.md +++ b/README.md @@ -37,26 +37,26 @@ Delegations MUST include a signature that validates against the `iss` DID. A Del | Field | Type | Required | Description | |---------|-------------|----------|----------------------------------------------| -| `ucd` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | +| `pld` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | | `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | # 3. Delegation Payload The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|-------|-------------------------------------------|----------|-------------------------------------------------------------| -| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `nnc` | `Bytes` | Yes | Nonce | -| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `cmd` | `String` | Yes | The [Command] to eventually invoke | -| `arg` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | -| `iff` | `[Caveat]` | Yes | Any additional [Caveat]s | +| Field | Type | Required | Description | +|--------|-------------------------------------------|----------|-------------------------------------------------------------| +| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| `nnc` | `Bytes` | Yes | Nonce | +| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `can` | `String` | Yes | The [Command] to eventually invoke | +| `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | +| `if` | `[Caveat]` | Yes | Any additional [Caveat]s | The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. @@ -148,7 +148,7 @@ Here is a simple example. ``` js { // ... - "nnc": {"/": {"bytes": "bGlnaHQgd29yay4"}} + "nonce": {"/": {"bytes": "bGlnaHQgd29yay4"}} } ``` @@ -163,7 +163,7 @@ Below is an example: ``` js { // ... - "mta": { + "meta": { "challenges": { "example.com": "abcdef", "another.example.net": "12345" @@ -179,12 +179,12 @@ Below is an example: Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -| Field | Type | Required | Description | -|-------|--------------------|----------|------------------------------------------------------------------------------------------------------| -| `sub` | `URI` | Yes | The [Subject] that this Capability is about | -| `cmd` | `Command` | Yes | The [Command] of this Capability | -| `arg` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | -| `iff` | `[{String : Any}]` | Yes | Any additional caveats, such as regex matchers, contextual information (e.g. day of week), and so on | +| Field | Type | Required | Description | +|--------|--------------------|----------|------------------------------------------------------------------------------------------------------| +| `sub` | `URI` | Yes | The [Subject] that this Capability is about | +| `can` | `Command` | Yes | The [Command] of this Capability | +| `args` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | +| `if` | `[{String : Any}]` | Yes | Any additional caveats, such as regex matchers, contextual information (e.g. day of week), and so on | Here is an illustrative example: @@ -192,12 +192,24 @@ Here is an illustrative example: { // ... "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" - "cmd": "msg/send", - "arg": { - "sender": "mailto:alice@example.com", - "subject": "Weekly Reports", + "can": "blog/post/create", + "args": { + "tags": ["weekend", "news"], + "status": "draft", }, - "iff": [{"day": "friday"}] + "if": [ + { + "day": "friday" + }, + { + "field": "title", + "match": "/^From the newsroom:/" + }, + { + "field": "body", + "max-chars": 2000 + } + ] } ``` @@ -223,8 +235,8 @@ By default, the Resource of a capability is the Subject. This makes the delegati ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource - "cmd": "crud/update", - "arg": {"status": "draft"} + "can": "crud/update", + "args": {"status": "draft"} // ... } ``` @@ -234,8 +246,8 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", - "cmd": "crud/create", - "arg":{ + "can": "crud/create", + "args":{ "uri": "https://example.com/blog/", // Resource "status": "draft" }, @@ -250,7 +262,7 @@ Commands MUST be presented as a case-insensitive string. By convention, Commands ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "cmd": "msg/send", + "can": "msg/send", "arg": { "from": "mailto:alice@example.com", "to": "mailto:bob@example.com" @@ -275,13 +287,6 @@ _"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be thought of as something akin to a super user permission in RBAC. -``` js -{ - "cmd": "*", - // ... -} -``` - The wildcard ability grants access to all other capabilities for the specified resource, across all possible namespaces. The wildcard ability is useful when "linking" agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delegate to update a Subject's mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others. ``` mermaid @@ -311,35 +316,124 @@ flowchart BT ... --> * ``` -## 4.4 Caveats +Conceptually it has this shape: + +``` ts +{ + "can": "*", + "args": { + "can": string, // Some other command + "args": {[string]: any} // That commad's arguments + }, + // ... +} +``` + +Since the nesting is fully redundant and infinitely nestable, it is instead used only in proof chains, and SHOULD NOT be invoked directly. -Caveats define a set of constraints on what can be re-delegated or invoked. Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each caveats MUST be formatted as a map. +## 4.4 Arguments + +The `args` field MAY contain partially applied Arguments for the shape of data specified by the [Command]. For example, below is an example that constrains sending email from a particular address: ``` js +// Delegation { - "sub": "did:web:example.com", - "cmd": "crud/update", - "iff": [ - { // ┐ - "uri": "https://blog.example.com", // │ - "status": "published" // ├─ Caveat - "tag": "news", // │ - }, // ┘ - { // ┐ - "tag": "breaking" // ├─ Caveat - } // ┘ - ], + "can": "msg/send", + "args": { + "from": "alice@example.com", + "subject": "Coffee" + }, + // ... +} + +// Valid invocation +{ + "do": "msg/send", + "args": { + "from": "alice@example.com", // Matches above + "to": ["bob@example.com", "carol@elsewhere.example.com"], + "subject": "Coffee", + "body": "Still on for coffee" // Matches above + }, + // ... +} +``` + +Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use [Caveats]. + +Note that this also applies to arrays and objects. For example, the `to` array in this example is considered to be exact, so the Invocation fails validation in this case: + +``` js +// Delegation +{ + "can": "msg/send", + "args": { + "from": "alice@example.com", + "to": ["bob@example.com"], + }, + // ... +} + +// INVALID Invocation +{ + "do": "msg/send", + "args": { + "from": "alice@example.com", // Matches above + "to": ["bob@example.com", "carol@elsewhere.example.com"], // Does not match exactly + "subject": "Coffee", + "body": "Still on for coffee" + }, // ... } ``` -The above MUST be interpreted as the set of capabilities below in the following table: +The indended logic is expressible with [Caveats]. + +## 4.5 Caveats + +Caveats (the `if` field) constrain the capability in two ways: -| Subject | Ability | Caveat | -|-----------------------|---------------|--------------------------------------------------------------------------------------------------------| -| `did:web:example.com` | `crud/update` | Posts at `https://blog.example.com` with the `published` status and tagged with `news` and `breaking` | +- Syntactic constraints on [Arguments] (length, regex, inclusion) +- Environmental / contextual conditions (day of week) -The `iff` caveat set MUST take the following shape: `[{}]`. The arrays represents logical `all` (chained `AND`s). To represent logical `any` / `OR`, issue another delegation with that attenuation. For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: +Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each caveat MUST be formatted as a map. + +``` js +// Delegation +{ + "sub": "did:web:example.com", + "can": "msg/send", + "args": { + "from": "alice@example.com" + } + "if": [ + { // ┐ + "day": "monday", // ├─ Caveat + }, // ┘ + { // ┐ + "field": "to", // ├─ Caveat + "includes": "bob@exmaple.com" // │ + } // ┘ + ], + // ... +} + +// Valid Invocation, if Monday +{ + "do": "msg/send", + "args": { + "from": "alice@example.com", + "to": ["bob@example.com", "carol@elsewhere.example.com"], // Matches criteria + "subject": "Coffee", + "body": "Still on for coffee" + }, + // ... +} +``` + +The above Delegation MUST be interpreted as "may send email from `alice@example.com` on Mondays as long as `bob@exmaple.com` is among the recipients" + +The `if` field MUST take the following shape: `[{}]`. The array represents a logical `all` (chained `AND`s). To represent logical `OR`, issue another delegation with that attenuation. For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: ``` js [ @@ -356,80 +450,48 @@ The `iff` caveat set MUST take the following shape: `[{}]`. The arrays represent ] ``` -Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, if a root UCAN caveat has a `tag` field but no (plural) `tags` field, it is still possible to express multiplicity by adding another `AND`ed caveat: +Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, below a caveat is added to the constraints on the `to` field. ``` js // Original Caveat [ { - "uri": "https://blog.example.com/", - "tag": "news" + "field": "to", + "includes": "bob@example.com" } ] // Attenuated Caveat [ - { - "uri": "https://blog.example.com/", - "tag": "news" - }, - // AND - { - "tag": "breaking" - } -] -``` - -In this example, the original caveat had not accounted for there being multiple tags at runtime. The attenuated capability grants access to blog posts at `https://blog.example.com/` that are tagged with both `news` and `breaking` due to the `AND` in the predicate. - -This is also helpful if each object has a special meaning or sense: - -``` js -[ - { - "type": "path", - "segments": ["blog", "october"] + { // Same as above + "field": "to", + "includes": "bob@example.com" }, - { - "type": "market", - "segments": ["manufacturing", "healthcare", "service", "technology"] - } -] -``` - -Note that while adding whole objects is useful in many situations (as above), attenuation MAY also be achieved by adding fields to an object: - -``` js -// Original Caveat -[ - { - "uri": "https://blog.example.com/", - "tag": "news" - } -] - -// Attenuated Caveat -[ - { - "uri": "https://blog.example.com/", - "tag": "news", - "status": "draft" // New field + { // Also must send to carol@example.com + "field": "to", + "includes": "carol@example.com" + }, + { // Only send to @example.com addresses + "field": "to", + "elements": { + "match": "/.+@example.com/" + } } ] ``` -### 4.4.1 The "True" Caveat +### 4.4.1 The Empty Caveat -The "True" caveat MUST represent the lack of caveat. In predicate logic terms, it represents `true`. It SHOULD be represented as `[{}]`, but `[]` MAY be used equivalently ("without any caveats"). +The "empty" caveat MUST represent the lack of caveats. This is equivalent to `true` in predicate terms. It SHOULD be represented as `[]`, but `[{}]` MAY be used equivalently ("without any caveats"). ``` js { - "iff": [{}], + "if": [], // ... } { - "iff": [], + "if": [{}], // ... } ``` @@ -599,7 +661,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Ability]: #43-ability -[Caveat]: #44-caveats +[Caveat]: #45-caveats [Envelope]: #2-delegation-envelope [Meta]: #35-meta [Payload]: #3-delegation-payload From 31e509ad4d4908b1b0faafb67435dc46deed6cfe Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 19 Oct 2023 18:41:56 -0700 Subject: [PATCH 081/134] Include the slash --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70fda2a..26ba3c8 100644 --- a/README.md +++ b/README.md @@ -275,9 +275,9 @@ Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A ### 4.3.1 Reserved Commands -#### 4.3.1.1 `ucan` Namespace +#### 4.3.1.1 `ucan/` Namespace -The `ucan` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan.*/` +The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/` Support for the `ucan/*` delegated proof ability is RECOMMENDED. From bf23571b459039fad3405562c03879f6a23859d3 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 31 Oct 2023 15:24:32 -0700 Subject: [PATCH 082/134] Caveat -> Condition --- README.md | 136 +++++++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 26ba3c8..11c533e 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # 0. Abstract -This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible caveats. +This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible conditions. # 1. Introduction -UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc caveats, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. +UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. @@ -44,19 +44,19 @@ Delegations MUST include a signature that validates against the `iss` DID. A Del The payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|--------|-------------------------------------------|----------|-------------------------------------------------------------| -| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `nnc` | `Bytes` | Yes | Nonce | -| `mta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `can` | `String` | Yes | The [Command] to eventually invoke | -| `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | -| `if` | `[Caveat]` | Yes | Any additional [Caveat]s | +| Field | Type | Required | Description | +|---------|-----------------------------------|----------|-------------------------------------------------------------| +| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| `nonce` | `Bytes` | Yes | Nonce | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `can` | `String` | Yes | The [Command] to eventually invoke | +| `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | +| `cond` | `[Condition]` | Yes | Any additional [Condition]s | The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. @@ -179,12 +179,12 @@ Below is an example: Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -| Field | Type | Required | Description | -|--------|--------------------|----------|------------------------------------------------------------------------------------------------------| -| `sub` | `URI` | Yes | The [Subject] that this Capability is about | -| `can` | `Command` | Yes | The [Command] of this Capability | -| `args` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | -| `if` | `[{String : Any}]` | Yes | Any additional caveats, such as regex matchers, contextual information (e.g. day of week), and so on | +| Field | Type | Required | Description | +|--------|--------------------|----------|---------------------------------------------------------------------------------------------------------| +| `sub` | `URI` | Yes | The [Subject] that this Capability is about | +| `can` | `Command` | Yes | The [Command] of this Capability | +| `args` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | +| `cond` | `[{String : Any}]` | Yes | Any additional conditions, such as regex matchers, contextual information (e.g. day of week), and so on | Here is an illustrative example: @@ -197,7 +197,7 @@ Here is an illustrative example: "tags": ["weekend", "news"], "status": "draft", }, - "if": [ + "cond": [ { "day": "friday" }, @@ -241,7 +241,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati } ``` -In the case where access to an [external resource] is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Caveat(s)][Caveat], except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor. +In the case where access to an [external resource] is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant [Conditions], except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor. ``` js { @@ -359,7 +359,7 @@ The `args` field MAY contain partially applied Arguments for the shape of data s } ``` -Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use [Caveats]. +Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use [Conditions]. Note that this also applies to arrays and objects. For example, the `to` array in this example is considered to be exact, so the Invocation fails validation in this case: @@ -387,16 +387,16 @@ Note that this also applies to arrays and objects. For example, the `to` array i } ``` -The indended logic is expressible with [Caveats]. +The indended logic is expressible with [Conditions]. -## 4.5 Caveats +## 4.5 Conditions -Caveats (the `if` field) constrain the capability in two ways: +The `cond` field MUST contain any additional conditions. This concept is sometimes called a "caveat". Conditions constrain the capability in two ways: - Syntactic constraints on [Arguments] (length, regex, inclusion) - Environmental / contextual conditions (day of week) -Caveat semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Caveats MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each caveat MUST be formatted as a map. +Condition semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Conditions MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each Condition MUST be formatted as a map. ``` js // Delegation @@ -406,12 +406,12 @@ Caveat semantics MUST be established by the Subject. They are openly extensible, "args": { "from": "alice@example.com" } - "if": [ + "cond": [ { // ┐ - "day": "monday", // ├─ Caveat + "day": "monday", // ├─ Condition }, // ┘ { // ┐ - "field": "to", // ├─ Caveat + "field": "to", // ├─ Condition "includes": "bob@exmaple.com" // │ } // ┘ ], @@ -438,22 +438,22 @@ The `if` field MUST take the following shape: `[{}]`. The array represents a log ``` js [ { // ┐ - a: 1, // ├─ Caveat ─┐ - b: 2 // │ │ - }, // ┘ ├─ AND ─┐ - { // ┐ │ │ - c: 3 // ├─ Caveat ─┘ │ - }, // ┘ ├─ AND - { // ┐ │ - d: 4 // ├─ Caveat ─────────┘ + a: 1, // ├─ Confition ─┐ + b: 2 // │ │ + }, // ┘ ├─ AND ─┐ + { // ┐ │ │ + c: 3 // ├─ Condition ─┘ │ + }, // ┘ ├─ AND + { // ┐ │ + d: 4 // ├─ Condition ─────────┘ } // ┘ ] ``` -Expressing caveats in this standard way simplifies ad hoc extension at delegation time. As a concrete example, below a caveat is added to the constraints on the `to` field. +Expressing Conditions in this standard way simplifies ad hoc extension at delegation time. As a concrete example, below a Condition is added to the constraints on the `to` field. ``` js -// Original Caveat +// Original Condition [ { "field": "to", @@ -461,7 +461,7 @@ Expressing caveats in this standard way simplifies ad hoc extension at delegatio } ] -// Attenuated Caveat +// Attenuated Conditions [ { // Same as above "field": "to", @@ -480,27 +480,15 @@ Expressing caveats in this standard way simplifies ad hoc extension at delegatio ] ``` -### 4.4.1 The Empty Caveat +### 4.4.1 The True Condition -The "empty" caveat MUST represent the lack of caveats. This is equivalent to `true` in predicate terms. It SHOULD be represented as `[]`, but `[{}]` MAY be used equivalently ("without any caveats"). - -``` js -{ - "if": [], - // ... -} - -{ - "if": [{}], - // ... -} -``` +The "true condition" MUST (vacuously) represent the lack of Conditions: it is equivalent to `true` in predicate terms. It SHOULD be expressed simply by not including a Condition, but MAY be expressed as `{}`. # 5 Validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. -Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a caveat fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. +Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a Condition fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: @@ -605,24 +593,24 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -## 5.3 Caveat Attenuation +## 5.3 Condition Attenuation -The caveat array SHOULD NOT be empty, as an empty array means "in no case" (which is equivalent to not listing the ability). This follows from the rule that delegations MUST be of equal or lesser scope. When an array is given, an attenuated caveat MUST (syntactically) include all of the fields of the relevant proof caveat, plus the newly introduced caveats. +The Condition array MAY be empty (which is equivelant to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: -Here are some abstract examples: +| Proof Conditions | Delegated Conditions | Is Valid? | Comment | +|----------------------|----------------------|-----------|------------------------------------------------------------| +| `[]` | `[]` | Yes | Equal | +| `[]` | `[{}]` | Yes | [True Condition] | +| `[{}]` | `[]` | Yes | [True Condition] | +| `[{}]` | `[{}]` | Yes | Equal | +| `[{a: 1}]` | `[{a: 1}]` | Yes | Equal | +| `[]` | `[{a: 1}]` | Yes | Adds a Condition | +| `[{a: 1}]` | `[{a: 1}, {a: 2}]` | Yes | Adds new Condition | +| `[{a: 1}]` | `[{}]` | No | Escalation by removing a Condition | +| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | No | Removes previous Conditions (despite having the same keys) | +| `[{a: 1}]` | `[{b: 2}]` | No | Removes original Condition | -| Proof Caveats | Delegated Caveats | Is Valid? | Comment | -|----------------------|--------------------|-----------|---------------------------------------| -| `[]` | `[]` | Yes | Equal | -| `[]` | `[{}]` | Yes | [True Caveat] | -| `[{}]` | `[]` | Yes | [True Caveat] | -| `[{}]` | `[{}]` | Yes | Equal | -| `[{a: 1}]` | `[{a: 1}]` | Yes | Equal | -| `[{}]` | `[{a: 1}]` | Yes | Attenuates `{}` by adding fields | -| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | Yes | Attenuates existing caveat | -| `[{a: 1}]` | `[{a: 1}, {a: 2}]` | Yes | Adds new caveat inside an `AND` block | -| `[{a: 1}]` | `[{}]]` | No | Escalation by removing fields | -| `[{a: 1}]` | `[{b: 2}]` | No | Escalation by replacing fields | +Conditions MAY be presented in any order, but merely appending to the array is RECOMMENDED. ## 5.4 Signature Validation @@ -661,12 +649,12 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Ability]: #43-ability -[Caveat]: #45-caveats +[Conditions]: #45-conditions [Envelope]: #2-delegation-envelope [Meta]: #35-meta [Payload]: #3-delegation-payload [Subject]: #41-subject -[True Caveat]: #441-the-true-caveat +[True Condition]: #441-the-true-condition [Wildcard Ability]: #4312--aka-wildcard From 59728251e00e8ceee750ceae77889c5f6689dfc0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:07:46 -0700 Subject: [PATCH 083/134] Update README.md Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com> Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11c533e..802ec73 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Below are a couple examples: ## 3.4 Nonce -The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures this uniqueness. +The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures uniqueness. The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's crypto suite. From 0ebc3399b4ffc6c47399d44d811e930775651d5c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:07:59 -0700 Subject: [PATCH 084/134] Update README.md Co-authored-by: Brian Ginsburg <7957636+bgins@users.noreply.github.com> Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 802ec73..16361a1 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Below are a couple examples: `nbf` and `exp` stand for "not before" and "expires at," respectively. These MUST be expressed as seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. -The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay using a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. +The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay invoking a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. The `exp` field MUST be set. Following the [principle of least authority][PoLA], it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid. From df7d62f90ba3530b2c9fe868b055dad5843d71ad Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:21:21 -0700 Subject: [PATCH 085/134] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16361a1..7bf6f15 100644 --- a/README.md +++ b/README.md @@ -514,7 +514,7 @@ const ensureTime = (delegationChain, now) => { ## 5.2 Principal Alignment -In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the originating principal for each resource. +In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the Subject for each resource. This calculation MUST NOT take into account [DID fragment]s. If present, fragments are only intended to clarify which of a DID's keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired. From 1e7e2dd0a98968d6bc003e7321e982413544a667 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:38:27 -0700 Subject: [PATCH 086/134] Remove nonsense paragraph --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 7bf6f15..dcc4b81 100644 --- a/README.md +++ b/README.md @@ -573,10 +573,6 @@ flowchart RL prf --> Delegations ``` -In the above diagram, Alice has some storage. This storage may exist in one location with a single source of truth, but to help build intuition this example is location independent: local versions and remote stored copies are eventually consistent, and there is no one "correct" copy. As such, we list the owner (Alice) directly on the resource. - -Alice delegates access to Bob. Bob then redelegates to Carol. Carol invokes the UCAN as part of a REST request to a compute service. To do this, she MUST both provide proof that she has access (the UCAN chain), and MUST delegate access to the invoking compute service. The invoking service MUST check that the root issuer (Alice) is in fact the owner (typically the creator) of the resource. This MAY be listed directly on the resource, as it is here. Once the UCAN chain and root ownership are validated, the storage service performs the write. - ### 5.2.1 Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. From 38e9c991cf29faacdad327db447d1c4270f25c34 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:39:53 -0700 Subject: [PATCH 087/134] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcc4b81..ea1b16d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Delegations MUST include a signature that validates against the `iss` DID. A Del | Field | Type | Required | Description | |---------|-------------|----------|----------------------------------------------| | `pld` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | -| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `ucd` field | +| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `pld` field | # 3. Delegation Payload From f59b4f5ba08fc0ff2c64160ebba9da81a9999779 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 2 Nov 2023 16:40:59 -0700 Subject: [PATCH 088/134] Spelling --- .custom-words.txt | 4 ++++ README.md | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.custom-words.txt b/.custom-words.txt index da3fcff..6bea5f0 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -94,6 +94,7 @@ Zelenka adoptability al alice +args async attenuations aud @@ -107,6 +108,7 @@ cacheability cid codec codecs +cond const const const @@ -160,6 +162,7 @@ lockboxes mDooWp mailto matcher +matchers memoization modelled msg @@ -173,6 +176,7 @@ nbf nbsp nbspAlice nbspDan +nestable nonces nota num diff --git a/README.md b/README.md index ea1b16d..b3fe190 100644 --- a/README.md +++ b/README.md @@ -387,7 +387,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i } ``` -The indended logic is expressible with [Conditions]. +The intended logic is expressible with [Conditions]. ## 4.5 Conditions @@ -412,7 +412,7 @@ Condition semantics MUST be established by the Subject. They are openly extensib }, // ┘ { // ┐ "field": "to", // ├─ Condition - "includes": "bob@exmaple.com" // │ + "includes": "bob@example.com" // │ } // ┘ ], // ... @@ -591,7 +591,7 @@ A good litmus test for invocation validity by a invoking agent is to check if th ## 5.3 Condition Attenuation -The Condition array MAY be empty (which is equivelant to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: +The Condition array MAY be empty (which is equivalent to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: | Proof Conditions | Delegated Conditions | Is Valid? | Comment | |----------------------|----------------------|-----------|------------------------------------------------------------| From 532d3d7337024abe8ab3a3f2dc608da5a1869470 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 7 Nov 2023 15:11:15 -0800 Subject: [PATCH 089/134] LOL monday --- .custom-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.custom-words.txt b/.custom-words.txt index 6bea5f0..bd8607a 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -165,6 +165,7 @@ matcher matchers memoization modelled +monday msg msgGraph multicodec From e27da0a980d6d4c24512333d75318d27271f62a2 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 18:00:45 +0200 Subject: [PATCH 090/134] Update format, add diagram --- README.md | 126 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index b3fe190..e067f61 100644 --- a/README.md +++ b/README.md @@ -21,50 +21,82 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. -# 0. Abstract +# Abstract This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible conditions. -# 1. Introduction +# Introduction UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. -# 2. Delegation (Envelope) +# Delegation (Envelope) Delegations MUST include a signature that validates against the `iss` DID. A Delegation Payload on its own MUST NOT be considered a valid Delegation. -| Field | Type | Required | Description | -|---------|-------------|----------|----------------------------------------------| -| `pld` | `&Payload` | Yes | The CID of the [Delegation Payload][Payload] | -| `sig` | `Signature` | Yes | The `iss`'s [Signature] over the `pld` field | +``` mermaid +flowchart TD + subgraph Delegation + SignatureBytes["Signature (raw bytes)"] + + subgraph SigPayload ["Signature Payload"] + VarsigHeader["Varsig Header"] + + subgraph DelegationPayload ["Delegation Payload"] + iss + sub + can + args + prf + etc["..."] + end + end + end +``` -# 3. Delegation Payload +``` js +{ + "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} + "p": { + "h": {"/": {"bytes": "NBIFEgEAcQ"}}, + "ucan/d/1.0.0-rc.1": delegationPayload + } +} +``` -The payload MUST describe the authorization claims, who is involved, and its validity period. +| Field | Type | Required | Description | +|-------|--------------------|----------|-------------------------------------------------------| +| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | +| `p` | `SignaturePayload` | Yes | The content that was signed | + +## Signature Payload + +| Field | Type | Required | Description | +|---------------------|---------------------|----------|--------------------------| +| `h` | `VarsigHeader` | Yes | The Varsig header | +| `ucan/d/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | + +## Delegation Payload + +The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. | Field | Type | Required | Description | |---------|-----------------------------------|----------|-------------------------------------------------------------| -| `udv` | `String` | Yes | UCAN Semantic Version (`1.0.0-rc.1`) | | `iss` | `DID` | Yes | Issuer DID (sender) | | `aud` | `DID` | Yes | Audience DID (receiver) | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | | `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | | `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | | `nonce` | `Bytes` | Yes | Nonce | | `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | | `can` | `String` | Yes | The [Command] to eventually invoke | | `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | | `cond` | `[Condition]` | Yes | Any additional [Condition]s | The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. -## 3.1 Version - -The `udv` field sets the version of the UCAN Delegation specification used in the payload. - -## 3.2 Principals +## Principals The `iss` and `aud` fields describe the token's principals. They are distinguished by having DIDs. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. @@ -98,7 +130,7 @@ Below are a couple examples: "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", ``` -## 3.3 Time Bounds +## Time Bounds `nbf` and `exp` stand for "not before" and "expires at," respectively. These MUST be expressed as seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. @@ -135,9 +167,9 @@ Below are a couple examples: } ``` -## 3.4 Nonce +## Nonce -The REQUIRED nonce parameter `nnc` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures uniqueness. +The REQUIRED nonce parameter `nonce` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures uniqueness. The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's crypto suite. @@ -152,7 +184,7 @@ Here is a simple example. } ``` -## 3.5 Meta +## Meta The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. @@ -175,7 +207,7 @@ Below is an example: } ``` -# 4. Capability +# Capability Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: @@ -213,7 +245,7 @@ Here is an illustrative example: } ``` -## 4.1 Subject +## Subject The Subject MUST be the DID that initiated the delegation chain. @@ -226,7 +258,7 @@ For example: } ``` -## 4.2 Resource +## Resource Unlike Subjects and Commands, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. @@ -255,7 +287,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS } ``` -## 4.3 Command +## Command Commands MUST be presented as a case-insensitive string. By convention, Commands SHOULD be namespaced with a slash, such as `msg/send`. @@ -273,15 +305,13 @@ Commands MUST be presented as a case-insensitive string. By convention, Commands Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A typical example is a superuser capability ("anything") on a file system. Another is mutation vs read access, such that in an HTTP context, `write` implies `put`, `patch`, `delete`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. -### 4.3.1 Reserved Commands +### Reserved Commands -#### 4.3.1.1 `ucan/` Namespace +#### `ucan/` Namespace The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/` -Support for the `ucan/*` delegated proof ability is RECOMMENDED. - -#### 4.3.1.2 `*` AKA "Wildcard" +#### `*` AKA "Wildcard" _"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ @@ -331,7 +361,7 @@ Conceptually it has this shape: Since the nesting is fully redundant and infinitely nestable, it is instead used only in proof chains, and SHOULD NOT be invoked directly. -## 4.4 Arguments +## Arguments The `args` field MAY contain partially applied Arguments for the shape of data specified by the [Command]. For example, below is an example that constrains sending email from a particular address: @@ -389,7 +419,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i The intended logic is expressible with [Conditions]. -## 4.5 Conditions +## Conditions The `cond` field MUST contain any additional conditions. This concept is sometimes called a "caveat". Conditions constrain the capability in two ways: @@ -480,11 +510,11 @@ Expressing Conditions in this standard way simplifies ad hoc extension at delega ] ``` -### 4.4.1 The True Condition +### The True Condition The "true condition" MUST (vacuously) represent the lack of Conditions: it is equivalent to `true` in predicate terms. It SHOULD be expressed simply by not including a Condition, but MAY be expressed as `{}`. -# 5 Validation +# Validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. @@ -492,7 +522,7 @@ Each capability has its own semantics, which needs to be interpretable by the [E If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: -## 5.1 Time Bounds +## Time Bounds A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegation chain valid between the latest `nbf` and earliest `exp`. @@ -512,7 +542,7 @@ const ensureTime = (delegationChain, now) => { } ``` -## 5.2 Principal Alignment +## Principal Alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the Subject for each resource. @@ -573,7 +603,7 @@ flowchart RL prf --> Delegations ``` -### 5.2.1 Recipient Validation +### Recipient Validation An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. @@ -589,7 +619,7 @@ The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmM A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. -## 5.3 Condition Attenuation +## Condition Attenuation The Condition array MAY be empty (which is equivalent to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: @@ -608,11 +638,11 @@ The Condition array MAY be empty (which is equivalent to saying "with no other c Conditions MAY be presented in any order, but merely appending to the array is RECOMMENDED. -## 5.4 Signature Validation +## Signature Validation -The `sig` field MUST validate against the `iss` DID from the [Payload]. +The [Signature] field MUST validate against the `iss` DID from the [Payload]. -# 6. Acknowledgments +# Acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. @@ -644,14 +674,14 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Ability]: #43-ability -[Conditions]: #45-conditions -[Envelope]: #2-delegation-envelope -[Meta]: #35-meta -[Payload]: #3-delegation-payload -[Subject]: #41-subject -[True Condition]: #441-the-true-condition -[Wildcard Ability]: #4312--aka-wildcard +[Ability]: #ability +[Conditions]: #conditions +[Envelope]: #delegation-envelope +[Meta]: #meta +[Payload]: #delegation-payload +[Subject]: #subject +[True Condition]: #the-true-condition +[Wildcard Ability]: #-aka-wildcard From 3bfb78836efa28c5fcfc1aceded7fed80c4fc741 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 18:04:17 +0200 Subject: [PATCH 091/134] Add links & consistency --- .custom-words.txt | 2 ++ README.md | 4 +++- delegation.ipldsch | 13 ++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.custom-words.txt b/.custom-words.txt index bd8607a..86465f0 100644 --- a/.custom-words.txt +++ b/.custom-words.txt @@ -77,6 +77,7 @@ SPKI ScopeA ScopeB Seitan +SignatureBytes Subschemes TXT TypeScript @@ -86,6 +87,7 @@ UCANs URI URIs Vandevelde +Varsig WG WebNative Webber diff --git a/README.md b/README.md index e067f61..94f11c2 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [DID] - [IPLD] - [UCAN] +- [Varsig] ## Language @@ -695,6 +696,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata +[Command]: https://github.com/ucan-wg/spec#33-command [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ @@ -717,7 +719,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Mikael Rogers]: https://github.com/mikeal/ [OCAP]: https://en.wikipedia.org/wiki/Object-capability_model [OCapN]: https://github.com/ocapn/ -[Command]: https://github.com/ucan-wg/spec#33-command [Philipp Krüger]: https://github.com/matheus23 [PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege [Protocol Labs]: https://protocol.ai/ @@ -731,6 +732,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Steven Vandevelde]: https://github.com/icidasset [UCAN Invocation]: https://github.com/ucan-wg/invocation [UCAN]: https://github.com/ucan-wg/spec +[Varsig]: https://github.com/ChainAgnostic/varsig [W3C]: https://www.w3.org/ [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ [`did:key`]: https://w3c-ccg.github.io/did-method-key/ diff --git a/delegation.ipldsch b/delegation.ipldsch index b303c72..f0b1c05 100644 --- a/delegation.ipldsch +++ b/delegation.ipldsch @@ -1,20 +1,19 @@ type Delegation struct { - dgn &DelegationPayload - sig &Signature + p SignaturePayload + s Signature } type DelegationPayload struct { - udv Semver - iss DID aud DID + sub DID exp Integer nbf Integer - sub DID - can String - iff [{String : Any}] + can String + args {String : Any} + cond [{String : Any}] } type Signature union { From 50ca0f0539c68506b1187657094a1563f00bf36a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 18:09:49 +0200 Subject: [PATCH 092/134] Expand example; integrate bengo's edit --- README.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 94f11c2..40ab9fe 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ This specification describes the semantics and serialization format for [UCAN] d UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. -Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what" can be done than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. +Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what can be done?" than a list of "who can do what?". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. # Delegation (Envelope) @@ -61,7 +61,25 @@ flowchart TD "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} "p": { "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/d/1.0.0-rc.1": delegationPayload + "ucan/d/1.0.0-rc.1": { + "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", + "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", + "can": "crud/create", + "args": { + "uri": "https://example.com/blog/posts", + "headers": { + "content-type": "application/json" + }, + "payload": { + "title": "UCAN for Fun an Profit", + "body": "UCAN is great!", + "topics": ["authz", "journal"], + "draft": true + } + }, + "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, + "exp": 1697409438 + } } } ``` @@ -87,13 +105,13 @@ The Delegation payload MUST describe the authorization claims, who is involved, | `iss` | `DID` | Yes | Issuer DID (sender) | | `aud` | `DID` | Yes | Audience DID (receiver) | | `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `nonce` | `Bytes` | Yes | Nonce | -| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | | `can` | `String` | Yes | The [Command] to eventually invoke | | `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | +| `nonce` | `Bytes` | Yes | Nonce | | `cond` | `[Condition]` | Yes | Any additional [Condition]s | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. From ac012620e4e5e4aae7a909476946546d111d4b35 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 18:12:12 +0200 Subject: [PATCH 093/134] Per Philipp, expand on why we'd want to reserve ucan/ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40ab9fe..f23047b 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A #### `ucan/` Namespace -The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/` +The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/`. This is important for keeping a space for community-blessed Commands in the future, such as standard library Commands. #### `*` AKA "Wildcard" From f36fdddf8448ddeb8d53db5f13938874972488e6 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 20 Dec 2023 18:20:45 +0200 Subject: [PATCH 094/134] Reference revocation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f23047b..7705a81 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A #### `ucan/` Namespace -The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/`. This is important for keeping a space for community-blessed Commands in the future, such as standard library Commands. +The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/`. This is important for keeping a space for community-blessed Commands in the future, such as standard library Commands, such as [Revocation]. #### `*` AKA "Wildcard" @@ -744,6 +744,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 [RS256]: https://www.rfc-editor.org/rfc/rfc7518#section-3.3 [Raw data multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L41 +[Revocation]: https://github.com/ucan-wg/revocation [SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html From 4f4f7c5d02099ccc03c0a89c8f3a105f96adad7b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 21 Dec 2023 12:01:43 +0200 Subject: [PATCH 095/134] s/subject/title/ per Bengo --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7705a81..f239c12 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,7 @@ The `args` field MAY contain partially applied Arguments for the shape of data s "can": "msg/send", "args": { "from": "alice@example.com", - "subject": "Coffee" + "title": "Coffee" }, // ... } @@ -401,7 +401,7 @@ The `args` field MAY contain partially applied Arguments for the shape of data s "args": { "from": "alice@example.com", // Matches above "to": ["bob@example.com", "carol@elsewhere.example.com"], - "subject": "Coffee", + "title": "Coffee", "body": "Still on for coffee" // Matches above }, // ... @@ -429,7 +429,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i "args": { "from": "alice@example.com", // Matches above "to": ["bob@example.com", "carol@elsewhere.example.com"], // Does not match exactly - "subject": "Coffee", + "title": "Coffee", "body": "Still on for coffee" }, // ... @@ -473,7 +473,7 @@ Condition semantics MUST be established by the Subject. They are openly extensib "args": { "from": "alice@example.com", "to": ["bob@example.com", "carol@elsewhere.example.com"], // Matches criteria - "subject": "Coffee", + "title": "Coffee", "body": "Still on for coffee" }, // ... From 8c69037cbe151bdbba301a0914d622d6cc7400ea Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 12:49:58 -0700 Subject: [PATCH 096/134] Starting policy language specification --- README.md | 418 ++++++++++++++---------------------------------------- 1 file changed, 105 insertions(+), 313 deletions(-) diff --git a/README.md b/README.md index f239c12..eace03b 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,18 @@ ## Editors -- [Brooklyn Zelenka], [Fission] +- [Brooklyn Zelenka], [Witchcraft Software] ## Authors -- [Brooklyn Zelenka], [Fission] +- [Brooklyn Zelenka], [Witchcraft Software] - [Daniel Holmgren], [Bluesky] - [Irakli Gozalishvili], [Protocol Labs] -- [Philipp Krüger], [Fission] +- [Philipp Krüger], [number zero] ## Dependencies -- [DID] -- [IPLD] - [UCAN] -- [Varsig] ## Language @@ -24,78 +21,20 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S # Abstract -This specification describes the semantics and serialization format for [UCAN] delegation between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and extensible conditions. - -# Introduction - -UCAN Delegation is a certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. - -Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what can be done?" than a list of "who can do what?". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. +This specification describes the representation and semantics for delegating attenuated authority between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and a minimal syntatically-driven policy langauge. -# Delegation (Envelope) - -Delegations MUST include a signature that validates against the `iss` DID. A Delegation Payload on its own MUST NOT be considered a valid Delegation. - -``` mermaid -flowchart TD - subgraph Delegation - SignatureBytes["Signature (raw bytes)"] - - subgraph SigPayload ["Signature Payload"] - VarsigHeader["Varsig Header"] - - subgraph DelegationPayload ["Delegation Payload"] - iss - sub - can - args - prf - etc["..."] - end - end - end -``` +# Introduction -``` js -{ - "s": {"/": {"bytes": "7aEDQLYvb3lygk9yvAbk0OZD0q+iF9c3+wpZC4YlFThkiNShcVriobPFr/wl3akjM18VvIv/Zw2LtA4uUmB5m8PWEAU"}} - "p": { - "h": {"/": {"bytes": "NBIFEgEAcQ"}}, - "ucan/d/1.0.0-rc.1": { - "iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", - "sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z", - "can": "crud/create", - "args": { - "uri": "https://example.com/blog/posts", - "headers": { - "content-type": "application/json" - }, - "payload": { - "title": "UCAN for Fun an Profit", - "body": "UCAN is great!", - "topics": ["authz", "journal"], - "draft": true - } - }, - "nonce": {"/": {"bytes": "TWFueSBopvcs"}}, - "exp": 1697409438 - } - } -} -``` +UCAN Delegation is a delegable certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. -| Field | Type | Required | Description | -|-------|--------------------|----------|-------------------------------------------------------| -| `s` | `Signature` | Yes | A signature by the Payload's `iss` over the `p` field | -| `p` | `SignaturePayload` | Yes | The content that was signed | +Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what can be done" than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. -## Signature Payload +# [UCAN Envelope] Configuration -| Field | Type | Required | Description | -|---------------------|---------------------|----------|--------------------------| -| `h` | `VarsigHeader` | Yes | The Varsig header | -| `ucan/d/1.0.0-rc.1` | `InvocationPayload` | Yes | The [Invocation Payload] | +## Type Tag +The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0-rc.1`. + ## Delegation Payload The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. @@ -105,126 +44,13 @@ The Delegation payload MUST describe the authorization claims, who is involved, | `iss` | `DID` | Yes | Issuer DID (sender) | | `aud` | `DID` | Yes | Audience DID (receiver) | | `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `can` | `String` | Yes | The [Command] to eventually invoke | +| `cmd` | `String` | Yes | The [Command] to eventually invoke | | `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | | `nonce` | `Bytes` | Yes | Nonce | -| `cond` | `[Condition]` | Yes | Any additional [Condition]s | +| `pol` | `[Policy]` | Yes | [Policies][Policy] | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | | `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | | `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | -| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | - -The payload MUST be serialized as [IPLD] and [signed over][Envelope]. The RECOMMENDED IPLD codec is [DAG-CBOR]. - -## Principals - -The `iss` and `aud` fields describe the token's principals. They are distinguished by having DIDs. These can be conceptualized as the sender and receiver of a postal letter. The token MUST be signed with the private key associated with the DID in the `iss` field. Implementations MUST include the [`did:key`] method, and MAY be augmented with [additional DID methods][DID]. - -The `iss` and `aud` fields MUST contain a single principal each. - -If an issuer's DID has multiple or mutable keys (e.g. [`did:plc`], [`did:web`]), the key used to sign the UCAN MUST be made explicit, using the [DID fragment] (the hash index) in the `iss` field. The `aud` field SHOULD NOT include a hash field, as this defeats the purpose of delegating to an identifier for multiple keys instead of an identity. - -[EdDSA] `did:key`s MUST be supported, and their use is RECOMMENDED. [RSA][did:key RSA] and [P-256 ECDSA][did:key ECDSA] `did:key`s MUST be supported, but SHOULD NOT be used when other options are available. - -Note that every [Subject] MUST correspond to a root delegation issuer. - -Below are a couple examples: - -```json -"aud": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", -"iss": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169", -``` - -```json -"aud": "did:web:example.com", -"iss": "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", -``` - -```json -"aud": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", -"iss": "did:web:example.com", -``` - -```json -"aud": "did:pkh:eth:0xb9c5714089478a327f09197987f16f9e5d936e8a", -"iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz", -``` - -## Time Bounds - -`nbf` and `exp` stand for "not before" and "expires at," respectively. These MUST be expressed as seconds since the Unix epoch 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. Due to limitations[^js-num-size] in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid. - -The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay invoking a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`. - -The `exp` field MUST be set. Following the [principle of least authority][PoLA], it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid. - -Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED. - -Several named points of time in the UCAN lifecycle can be found in the [high level spec][UCAN]. - -Below are a couple examples: - -```js -{ - // ... - "nbf": 1529496683, - "exp": 1575606941 -} -``` - -```js -{ - // ... - "exp": 1575606941 -} -``` - -```js -{ - // ... - "nbf": 1529496683, - "exp": null -} -``` - -## Nonce - -The REQUIRED nonce parameter `nonce` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures uniqueness. - -The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID's crypto suite. - -This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the [`mta`][Meta] field for more. - -Here is a simple example. - -``` js -{ - // ... - "nonce": {"/": {"bytes": "bGlnaHQgd29yay4"}} -} -``` - -## Meta - -The OPTIONAL `mta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc. - -The data contained in this map MUST NOT be semantically meaningful to delegation chains. - -Below is an example: - -``` js -{ - // ... - "meta": { - "challenges": { - "example.com": "abcdef", - "another.example.net": "12345" - }, - "sha3_256": { - "B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9": "hello world" - } - } -} -``` # Capability @@ -233,9 +59,8 @@ Capabilities are the semantically-relevant claims of a delegation. They MUST be | Field | Type | Required | Description | |--------|--------------------|----------|---------------------------------------------------------------------------------------------------------| | `sub` | `URI` | Yes | The [Subject] that this Capability is about | -| `can` | `Command` | Yes | The [Command] of this Capability | -| `args` | `{String : Any}` | Yes | Any arguments that MUST be present _verbatim_ in the [Invocation] | -| `cond` | `[{String : Any}]` | Yes | Any additional conditions, such as regex matchers, contextual information (e.g. day of week), and so on | +| `cmd` | `Command` | Yes | The [Command] of this Capability | +| `pol` | `[Policy]` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language] | Here is an illustrative example: @@ -243,23 +68,14 @@ Here is an illustrative example: { // ... "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp" - "can": "blog/post/create", - "args": { - "tags": ["weekend", "news"], - "status": "draft", - }, - "cond": [ - { - "day": "friday" - }, - { - "field": "title", - "match": "/^From the newsroom:/" - }, - { - "field": "body", - "max-chars": 2000 - } + "cmd": "/blog/post/create", + "pol": [ + ["==", ".status", "draft"], + ["every", ".reviewer", ["match", ".", "*@example.com"]], + ["some", ".tags", + ["or", + ["==", ".", "news"], + ["==", ".", "press"]]] ] } ``` @@ -286,8 +102,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource - "can": "crud/update", - "args": {"status": "draft"} + "cmd": "/crud/update", // ... } ``` @@ -297,107 +112,84 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` js { "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", - "can": "crud/create", - "args":{ - "uri": "https://example.com/blog/", // Resource - "status": "draft" - }, + "cmd": "/crud/create", + "pol": [ + ["==", ".uri", "https://example.com/blog/"], // Resource + ["==", ".status", "draft"] + ], // ... } ``` -## Command +# Policy Language -Commands MUST be presented as a case-insensitive string. By convention, Commands SHOULD be namespaced with a slash, such as `msg/send`. +UCAN Delegation uses a minimal predicate language as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. -``` js -{ - "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": { - "can": "msg/send", - "arg": { - "from": "mailto:alice@example.com", - "to": "mailto:bob@example.com" - }, - // ... -} -``` +The grammar of the UCAN Policy Language is as follows: -Abilities MAY be organized in a hierarchy that abstract over many [Command]s. A typical example is a superuser capability ("anything") on a file system. Another is mutation vs read access, such that in an HTTP context, `write` implies `put`, `patch`, `delete`, and so on. [`*` gives full access][Wildcard Ability] to a Resource more in the vein of object capabilities. Organizing abilities this way allows Resources to evolve over time in a backward-compatible manner, avoiding the need to reissue UCANs with new resource semantics. +## Syntax -### Reserved Commands +``` abnf +policy = "[" *condition "]" +condition = equality + / inequality + / match + / connective + / quantifier -#### `ucan/` Namespace +;; STRUCTURAL -The `ucan/` ability namespace MUST be reserved. This MUST include any ability string matching the regex `/^ucan\/.*/`. This is important for keeping a space for community-blessed Commands in the future, such as standard library Commands, such as [Revocation]. +connective = "['not', " policy "]" ; Negation + / "['and', " policy "]" ; Conjuction + / "['or', " policy "]" ; Disjunction -#### `*` AKA "Wildcard" +quanitifier = "['every', " selector ", " policy "]" ; Universal + / "['some', " selector ", " policy "]" ; Existential -_"Wildcard" (`*`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly._ +;; COMPARISONS -The "wildcard" (or "any", or "top") ability MUST be denoted `*`. This can be thought of as something akin to a super user permission in RBAC. +equality = "['==', " selector ", " ipld "]" ; Strict equality +inequality = "['>', " selector ", " number "]" ; Greater-than + / "['>=', " selector ", " number "]" ; Greater-than-or-equal-to + / "['<', " selector ", " number "]" ; Lesser-than + / "['<=', " selector ", " number "]" ; Lesser-than-or-equal-to -The wildcard ability grants access to all other capabilities for the specified resource, across all possible namespaces. The wildcard ability is useful when "linking" agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delegate to update a Subject's mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others. +match = "['match', " selector ", " pattern "]" -``` mermaid -%%{ init: { 'flowchart': { 'curve': 'linear' } } }%% - -flowchart BT - * - - msg/* --> * - subgraph msgGraph [ ] - msg/send --> msg/* - msg/receive --> msg/* - end - - crud/* --> * - subgraph crudGraph [ ] - crud/read --> crud/* - crud/mutate --> crud/* - - subgraph mutationGraph [ ] - crud/create --> crud/mutate - crud/update --> crud/mutate - crud/destroy --> crud/mutate - end - end +;; LITERALS - ... --> * -``` +selector = "." ; "This" + / *(".") *subselector ; Lens -Conceptually it has this shape: +subselector = "." 1*utf8 ; Dotted field selector + / "['" string "']" ; Explicit field selector + / "[" integer "]" ; Index selector -``` ts -{ - "can": "*", - "args": { - "can": string, // Some other command - "args": {[string]: any} // That commad's arguments - }, - // ... -} +pattern = utf8 +number = integer / float ``` -Since the nesting is fully redundant and infinitely nestable, it is instead used only in proof chains, and SHOULD NOT be invoked directly. +## Semantics + + + -## Arguments -The `args` field MAY contain partially applied Arguments for the shape of data specified by the [Command]. For example, below is an example that constrains sending email from a particular address: ``` js // Delegation { - "can": "msg/send", - "args": { - "from": "alice@example.com", - "title": "Coffee" - }, + "cmd": "/msg/send", + "pol": [ + ["==", ".from", "alice@example.com" + ["==", ".title", "Coffee"] + ], // ... } // Valid invocation { - "do": "msg/send", + "cmd": "/msg/send", "args": { "from": "alice@example.com", // Matches above "to": ["bob@example.com", "carol@elsewhere.example.com"], @@ -415,20 +207,32 @@ Note that this also applies to arrays and objects. For example, the `to` array i ``` js // Delegation { - "can": "msg/send", + "cmd": "/msg/send", + "pol": [ + ["==", ".from", "alice@example.com"], + ["some", ".to", ["match", ".", "*@example.com"]] + ] + // ... +} + +// VALID Invocation +{ + "cmd": "/msg/send", "args": { "from": "alice@example.com", - "to": ["bob@example.com"], + "to": ["bob@example.com", "carol@elsewhere.example.com"], + "title": "Coffee", + "body": "Still on for coffee" }, // ... } // INVALID Invocation { - "do": "msg/send", + "cmd": "/msg/send", "args": { - "from": "alice@example.com", // Matches above - "to": ["bob@example.com", "carol@elsewhere.example.com"], // Does not match exactly + "from": "alice@example.com", + "to": ["carol@elsewhere.example.com"], // Missing `*@example.com` "title": "Coffee", "body": "Still on for coffee" }, @@ -440,6 +244,7 @@ The intended logic is expressible with [Conditions]. ## Conditions +// FIXME The `cond` field MUST contain any additional conditions. This concept is sometimes called a "caveat". Conditions constrain the capability in two ways: - Syntactic constraints on [Arguments] (length, regex, inclusion) @@ -451,18 +256,16 @@ Condition semantics MUST be established by the Subject. They are openly extensib // Delegation { "sub": "did:web:example.com", - "can": "msg/send", + "cmd": "msg/send", "args": { "from": "alice@example.com" } - "cond": [ - { // ┐ - "day": "monday", // ├─ Condition - }, // ┘ - { // ┐ - "field": "to", // ├─ Condition - "includes": "bob@example.com" // │ - } // ┘ + "pol": ["or", + // no recipient can have a `@gmail.com` email address + ["every", ".to", ["not", ["match", ".", "*@competitor.example.com"]]], + + // or at least one `bcc` field must include @ourteam.example.com + ["some", ".bcc", ["match", ".", "*@ourteam.example.com"]] ], // ... } @@ -529,10 +332,6 @@ Expressing Conditions in this standard way simplifies ad hoc extension at delega ] ``` -### The True Condition - -The "true condition" MUST (vacuously) represent the lack of Conditions: it is equivalent to `true` in predicate terms. It SHOULD be expressed simply by not including a Condition, but MAY be expressed as `{}`. - # Validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. @@ -640,20 +439,11 @@ A good litmus test for invocation validity by a invoking agent is to check if th ## Condition Attenuation +FIXME + The Condition array MAY be empty (which is equivalent to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: -| Proof Conditions | Delegated Conditions | Is Valid? | Comment | -|----------------------|----------------------|-----------|------------------------------------------------------------| -| `[]` | `[]` | Yes | Equal | -| `[]` | `[{}]` | Yes | [True Condition] | -| `[{}]` | `[]` | Yes | [True Condition] | -| `[{}]` | `[{}]` | Yes | Equal | -| `[{a: 1}]` | `[{a: 1}]` | Yes | Equal | -| `[]` | `[{a: 1}]` | Yes | Adds a Condition | -| `[{a: 1}]` | `[{a: 1}, {a: 2}]` | Yes | Adds new Condition | -| `[{a: 1}]` | `[{}]` | No | Escalation by removing a Condition | -| `[{a: 1}], [{b: 2}]` | `[{a: 1, b: 2}]` | No | Removes previous Conditions (despite having the same keys) | -| `[{a: 1}]` | `[{b: 2}]` | No | Removes original Condition | +FIXME Conditions MAY be presented in any order, but merely appending to the array is RECOMMENDED. @@ -694,13 +484,12 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Ability]: #ability -[Conditions]: #conditions [Envelope]: #delegation-envelope [Meta]: #meta [Payload]: #delegation-payload [Subject]: #subject -[True Condition]: #the-true-condition -[Wildcard Ability]: #-aka-wildcard +[Policy]: #policy-language +[Policy Language]: #policy-language @@ -753,6 +542,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [UCAN]: https://github.com/ucan-wg/spec [Varsig]: https://github.com/ChainAgnostic/varsig [W3C]: https://www.w3.org/ +[Witchcraft Software]: https://github.com/expede [ZCAP-LD]: https://w3c-ccg.github.io/zcap-spec/ [`did:key`]: https://w3c-ccg.github.io/did-method-key/ [`did:plc`]: https://github.com/did-method-plc/did-method-plc @@ -763,5 +553,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems +[number zero]: https://n0.computer/ [revocation]: https://github.com/ucan-wg/revocation [ucan.xyz]: https://ucan.xyz +[UCAN Envelope]: https://github.com/ucan-wg/spec/blob/high-level/README.md#envelope From b0db83ef93e035cce47c768765ef46b968b3b4fc Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 14:46:54 -0700 Subject: [PATCH 097/134] Startnig on selectors --- README.md | 125 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index eace03b..5bf1d4a 100644 --- a/README.md +++ b/README.md @@ -39,28 +39,27 @@ The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0-rc.1`. The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|---------|-----------------------------------|----------|-------------------------------------------------------------| -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `cmd` | `String` | Yes | The [Command] to eventually invoke | -| `args` | `{String : Any}` | Yes | Any [Arguments] that MUST be present in the Invocation | -| `nonce` | `Bytes` | Yes | Nonce | -| `pol` | `[Policy]` | Yes | [Policies][Policy] | -| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| Field | Type | Required | Description | +|---------|------------------------------------------|----------|-------------------------------------------------------------| +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `cmd` | `String` | Yes | The [Command] to eventually invoke | +| `pol` | `Policy` | Yes | [Policy] | +| `nonce` | `Bytes` | Yes | Nonce | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| nul` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | # Capability Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -| Field | Type | Required | Description | -|--------|--------------------|----------|---------------------------------------------------------------------------------------------------------| -| `sub` | `URI` | Yes | The [Subject] that this Capability is about | -| `cmd` | `Command` | Yes | The [Command] of this Capability | -| `pol` | `[Policy]` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language] | +| Field | Type | Required | Description | +|--------|-----------|----------|--------------------------------------------------------------------------------------------------| +| `sub` | `DID` | Yes | The [Subject] that this Capability is about | +| `cmd` | `Command` | Yes | The [Command] of this Capability | +| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language] | Here is an illustrative example: @@ -71,7 +70,7 @@ Here is an illustrative example: "cmd": "/blog/post/create", "pol": [ ["==", ".status", "draft"], - ["every", ".reviewer", ["match", ".", "*@example.com"]], + ["every", ".reviewer", ["match", ".email", "*@example.com"]], ["some", ".tags", ["or", ["==", ".", "news"], @@ -125,13 +124,15 @@ In the case where access to an [external resource] is delegated, the Subject MUS UCAN Delegation uses a minimal predicate language as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. + The grammar of the UCAN Policy Language is as follows: ## Syntax + ``` abnf -policy = "[" *condition "]" -condition = equality +policy = "[" *predicate "]" +predicate = equality / inequality / match / connective @@ -148,33 +149,94 @@ quanitifier = "['every', " selector ", " policy "]" ; Universal ;; COMPARISONS -equality = "['==', " selector ", " ipld "]" ; Strict equality -inequality = "['>', " selector ", " number "]" ; Greater-than - / "['>=', " selector ", " number "]" ; Greater-than-or-equal-to - / "['<', " selector ", " number "]" ; Lesser-than - / "['<=', " selector ", " number "]" ; Lesser-than-or-equal-to +equality = "['==', " selector ", " ipld "]" ; Equality on IPLD literals +inequality = "['>', " selector ", " number "]" ; Numeric greater-than + / "['>=', " selector ", " number "]" ; Numeric greater-than-or-equal + / "['<', " selector ", " number "]" ; Numeric lesser-than + / "['<=', " selector ", " number "]" ; Numeric lesser-than-or-equal -match = "['match', " selector ", " pattern "]" +match = "['match', " selector ", " pattern "]" ; String wildcard matching -;; LITERALS +;; SELECTORS -selector = "." ; "This" - / *(".") *subselector ; Lens +selector = "." ; Identity + / *(".") 1*(subselector) ; Nested subselectors subselector = "." 1*utf8 ; Dotted field selector / "['" string "']" ; Explicit field selector / "[" integer "]" ; Index selector -pattern = utf8 +;; SPECIAL LITERALS + +pattern = *utf8 number = integer / float ``` +### Pattern + ## Semantics +A Policy is always given as an array of predicates. This top-level array is implicitly treated as a logical `and`, where `args` MUST pass validation of every top-level predicate. +Policies are structured as trees. With the exception of subtrees under `some`, `or`, and `not`, every leaf MUST evaluate to `true`. `some`, `or`, and `not` must +### Selectors +Selector syntax is closely based on a subset of [jq]. They operate on an [Invocation]'s `args` object. +For example, consider the following: + +``` js +// Invocation +{ + "args": { + "from": "alice@example.com", + "to": ["bob@example.com", "carol@elsewhere.example.com"], + "cc": ["fraud@example.com"], + "title": "Meeting Confirmation", + "body": "I'll see you on Tuesday" + }, + // ... +} +``` + + + + + + + + + + + + + + + + + + + + + + + +
SelectorValue
`.` + ```js + { + "from": "alice@example.com", + "to": ["bob@example.com", "carol@elsewhere.example.com"], + "cc": ["fraud@example.com"], + "title": "Meeting Confirmation", + "body": "I'll see you on Tuesday" + } + ``` +
`.title``"Meeting Confirmation"`
`.to[1]``"carol@elsewhere.example.com"`
+ +### Validation + +## Examples ``` js // Delegation @@ -538,6 +600,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html [Steven Vandevelde]: https://github.com/icidasset +[UCAN Envelope]: https://github.com/ucan-wg/spec/blob/high-level/README.md#envelope [UCAN Invocation]: https://github.com/ucan-wg/invocation [UCAN]: https://github.com/ucan-wg/spec [Varsig]: https://github.com/ChainAgnostic/varsig @@ -553,7 +616,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa [external resource]: https://github.com/ucan-wg/spec#55-wrapping-existing-systems +[jq]: https://jqlang.github.io/jq/ [number zero]: https://n0.computer/ [revocation]: https://github.com/ucan-wg/revocation [ucan.xyz]: https://ucan.xyz -[UCAN Envelope]: https://github.com/ucan-wg/spec/blob/high-level/README.md#envelope From e2aef559eb7ebbeb96e1f7b160d66d9dcc6a676b Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 14:49:03 -0700 Subject: [PATCH 098/134] Debugging GH rendering --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bf1d4a..f61fe2a 100644 --- a/README.md +++ b/README.md @@ -209,8 +209,11 @@ For example, consider the following: - `.` + `.` + + + ```js { "from": "alice@example.com", @@ -220,6 +223,7 @@ For example, consider the following: "body": "I'll see you on Tuesday" } ``` + From aec7259c3e3dff4011be9aef4ae6af07a3b285fb Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 14:50:33 -0700 Subject: [PATCH 099/134] More of same --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f61fe2a..5324b42 100644 --- a/README.md +++ b/README.md @@ -210,19 +210,22 @@ For example, consider the following: + `.` + + - ```js - { - "from": "alice@example.com", - "to": ["bob@example.com", "carol@elsewhere.example.com"], - "cc": ["fraud@example.com"], - "title": "Meeting Confirmation", - "body": "I'll see you on Tuesday" - } - ``` +```js +{ + "from": "alice@example.com", + "to": ["bob@example.com", "carol@elsewhere.example.com"], + "cc": ["fraud@example.com"], + "title": "Meeting Confirmation", + "body": "I'll see you on Tuesday" +} +``` From 0a64f54d64d084589a037a9b355a9e0f97cf6180 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 14:51:34 -0700 Subject: [PATCH 100/134] Same --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5324b42..48b41fe 100644 --- a/README.md +++ b/README.md @@ -231,13 +231,29 @@ For example, consider the following: - `.title` - `"Meeting Confirmation"` + + + `.title` + + + + + `"Meeting Confirmation"` + + - `.to[1]` - `"carol@elsewhere.example.com"` + + + `.to[1]` + + + + + `"carol@elsewhere.example.com" + + ` From 5f4d5646ea263c3ae94d07773cac6540747b87af Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 15:16:30 -0700 Subject: [PATCH 101/134] Figured out the table formatting --- README.md | 128 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 48b41fe..89a30f6 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ For example, consider the following: { "args": { "from": "alice@example.com", - "to": ["bob@example.com", "carol@elsewhere.example.com"], + "to": ["bob@example.com", "carol@not.example.com", "dan@example.com"], "cc": ["fraud@example.com"], "title": "Meeting Confirmation", "body": "I'll see you on Tuesday" @@ -201,60 +201,114 @@ For example, consider the following: ``` - - - - - - + + + + + + + - - + + +``` json +"." +``` - + + + + + - +``` json +".title" +``` + + + + - + - - + - + +``` json +".cc" +``` + + - - - + - ` - + + + + + + + + + +
SelectorValue
SelectorReturned Value
- - `.` +
- + -```js +```json { "from": "alice@example.com", - "to": ["bob@example.com", "carol@elsewhere.example.com"], + "to": ["bob@example.com", "carol@not.example.com", "dan@example.com"], "cc": ["fraud@example.com"], "title": "Meeting Confirmation", "body": "I'll see you on Tuesday" } ``` + +
-
-
+``` json +"Meeting Confirmation" +``` - `.title` +
+
- `"Meeting Confirmation"` - -
- - `.to[1]` - - - - `"carol@elsewhere.example.com" + + +``` json +["fraud@example.com"] +``` + +
+ +``` json +".to[1]" +``` + + + + +``` json +"carol@not.example.com" +``` + +
+ +``` json +".to[-1]" +``` + + + + +``` json +"dan@example.com" +``` + +
### Validation From 743323b197e7c82268a49bbbca19284b28a2e437 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 15:20:21 -0700 Subject: [PATCH 102/134] Add `?` case to the table --- README.md | 50 +++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 89a30f6..dfd9fc5 100644 --- a/README.md +++ b/README.md @@ -184,10 +184,9 @@ Policies are structured as trees. With the exception of subtrees under `some`, ` Selector syntax is closely based on a subset of [jq]. They operate on an [Invocation]'s `args` object. -For example, consider the following: +For example, consider the following `args` from an `Invocation`: -``` js -// Invocation +``` json { "args": { "from": "alice@example.com", @@ -214,13 +213,7 @@ NOTE: You cannot add _any_ indentation to this table if you want - - -``` json -"." -``` - - +
"."
```json @@ -237,13 +230,7 @@ NOTE: You cannot add _any_ indentation to this table if you want - - -``` json -".title" -``` - - +
".title"
@@ -255,15 +242,7 @@ NOTE: You cannot add _any_ indentation to this table if you want - - - -``` json -".cc" -``` - - - +
".cc"
``` json @@ -274,16 +253,9 @@ NOTE: You cannot add _any_ indentation to this table if you want +
".to[1]"
-``` json -".to[1]" -``` - - - - - ``` json "carol@not.example.com" ``` @@ -292,18 +264,22 @@ NOTE: You cannot add _any_ indentation to this table if you want +
".to[-1]"
``` json -".to[-1]" +"dan@example.com" ``` + + + +
".to[99]?"
- ``` json -"dan@example.com" +null ``` From 374b732c8992d2b9da2f8d9bc3787b72955a4cff Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 2 Jul 2024 15:49:00 -0700 Subject: [PATCH 103/134] Example evaluation --- README.md | 161 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 144 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index dfd9fc5..3ed8641 100644 --- a/README.md +++ b/README.md @@ -194,8 +194,7 @@ For example, consider the following `args` from an `Invocation`: "cc": ["fraud@example.com"], "title": "Meeting Confirmation", "body": "I'll see you on Tuesday" - }, - // ... + } } ``` @@ -279,6 +278,7 @@ NOTE: You cannot add _any_ indentation to this table if you want ``` json +// FIXME double check with implementation null ``` @@ -289,32 +289,159 @@ null ### Validation -## Examples +Validation -``` js -// Delegation -{ - "cmd": "/msg/send", - "pol": [ - ["==", ".from", "alice@example.com" - ["==", ".title", "Coffee"] - ], - // ... -} +Being tree structured, evaluation MAY proceed in any order. Here is one step-by-step example: -// Valid invocation -{ +
+Example Context + +``` js +{ // Invocation "cmd": "/msg/send", "args": { - "from": "alice@example.com", // Matches above + "from": "alice@example.com", "to": ["bob@example.com", "carol@elsewhere.example.com"], "title": "Coffee", - "body": "Still on for coffee" // Matches above + "body": "Still on for coffee" }, // ... } + +{ // Delegation + "cmd": "/msg", + "pol": [ + ["==", ".from", "alice@example.com"], + ["some", ".to", ["match", ".", "*@example.com"]] + ], + // ... +} ``` +
+ +
+Code Example + +``` js +// Extract policy +[ + ["==", ".from", "alice@example.com"], + ["some", ".to", ["match", ".", "*@example.com"]] +] + +// Resolve selectors +[ + ["==", "alice@example.com", "alice@example.com"], + ["some", ["bob@example.com", "carol@elsewhere.example.com"], ["match", ".", "*@example.com"]] +] + +// Expand quantifier +[ + ["==", "alice@example.com", "alice@example.com"], + ["or", [ + ["match", "bob@example.com", "*@example.com"] + ["match", "carol@elsewhere.example.com", "*@example.com"]]] +] + +// Evaluate first clause +[ + true, + ["or", [ + ["match", "bob@example.com", "*@example.com"] + ["match", "carol@elsewhere.example.com", "*@example.com"]]] +] + +// Evaluate second clause's children +[ + true, + ["or", [true, false]] +] + +// Evaluate second clause +[true, true] + +// Implicit top-level `and` +true +``` + +
+ +
+ +Graphical Example + +``` mermaid +flowchart BT + policy + eq["=="] --> policy + from[".from"] --> eq + alice["alice@example.com"] --> eq + + some --> policy + to[".to"] --> some + match --> some + this["."] --> match + pattern["*@example.com"] --> match +``` + +``` mermaid +flowchart BT + policy + eq["=="] --> policy + from["alice@example.com"] --> eq + alice["alice@example.com"] --> eq + + some --> policy + to["[]"] --> some + bob["bob@example.com"] --> to + carol["carol@not.example.com"] --> to + match --> some + this["."] --> match + pattern["*@example.com"] --> match +``` + +``` mermaid +flowchart BT + policy + eq["=="] --> policy + from["alice@example.com"] --> eq + alice["alice@example.com"] --> eq + + or --> policy + match_bob --> or + bob["bob@example.com"] --> match_bob + bob_pattern["*@example.com"] --> match_bob + + match_carol --> or + carol["carol@not.example.com"] --> match_carol + carol_pattern["*@example.com"] --> match_carol +``` + +``` mermaid +flowchart BT + policy + alice["true"] --> policy + + or --> policy + match_bob["true"] --> or + match_carol["false"] --> or +``` + +``` mermaid +flowchart BT + policy + alice["true"] --> policy + or["true"] --> policy +``` + +``` mermaid +flowchart BT + true +``` + +
+ Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use [Conditions]. Note that this also applies to arrays and objects. For example, the `to` array in this example is considered to be exact, so the Invocation fails validation in this case: From 1c0fbf88ade3a220e0b5f073e991841606c5f45a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 3 Jul 2024 11:46:00 -0700 Subject: [PATCH 104/134] Expand predicate logic section --- README.md | 336 +++++++++++++++++++++++++++--------------------------- 1 file changed, 169 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index 3ed8641..569968e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# UCAN Delegation Specification v1.0.0-rc.1 +# UCAN Delegation Specification +## Version 1.0.0-rc.1 ## Editors @@ -113,7 +114,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", "cmd": "/crud/create", "pol": [ - ["==", ".uri", "https://example.com/blog/"], // Resource + ["==", ".url", "https://example.com/blog/"], // Resource managed by the Subject ["==", ".status", "draft"] ], // ... @@ -122,17 +123,19 @@ In the case where access to an [external resource] is delegated, the Subject MUS # Policy Language -UCAN Delegation uses a minimal predicate language as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. +UCAN Delegation uses predicate logic statements extended with [jq]-style selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. +A Policy is always given as an array of predicates. This top-level array is implicitly treated as a logical `and`, where `args` MUST pass validation of every top-level predicate. -The grammar of the UCAN Policy Language is as follows: +Policies are structured as trees. With the exception of subtrees under `some`, `or`, and `not`, every leaf MUST evaluate to `true`. `some`, `or`, and `not` must -## Syntax +A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for nergation which MUST take the form `["not", term]`. +Below is a formal syntax for the UCAN Policy Language given in [ABNF] (for DAG-JSON): ``` abnf -policy = "[" *predicate "]" -predicate = equality +policy = "[" *1(statement *(", " statement)) "]" +statement = equality / inequality / match / connective @@ -140,15 +143,16 @@ predicate = equality ;; STRUCTURAL -connective = "['not', " policy "]" ; Negation - / "['and', " policy "]" ; Conjuction - / "['or', " policy "]" ; Disjunction +connective = "['not', " statement "]" ; Negation + / "['and', [" statement *(", " statement) "]]" ; Conjuction + / "['or', [" statement *(", " statement) "]]" ; Disjunction quanitifier = "['every', " selector ", " policy "]" ; Universal / "['some', " selector ", " policy "]" ; Existential ;; COMPARISONS +// FIXME DQUOTE equality = "['==', " selector ", " ipld "]" ; Equality on IPLD literals inequality = "['>', " selector ", " number "]" ; Numeric greater-than / "['>=', " selector ", " number "]" ; Numeric greater-than-or-equal @@ -171,14 +175,123 @@ subselector = "." 1*utf8 ; Dotted field selector pattern = *utf8 number = integer / float ``` + +### Comparisons -### Pattern +| Operator | Argument(s) | Example | +|----------|-------------------------------|----------------------------------| +| `==` | `Selector, IPLD` | `["==", ".a", [1, 2, {"b": 3}]]` | +| `<` | `Selector, (float | integer)` | `["<", ".a", 1]` | +| `<=` | `Selector, (float | integer)` | `["<=", ".a", 1]` | +| `>` | `Selector, (float | integer)` | `[">", ".a", 1]` | +| `>=` | `Selector, (float | integer)` | `[">=", ".a", 1]` | -## Semantics +Literal equality (`==`) MUST match the resolved selecor to entire IPLD argument. This is a "deep comparison". -A Policy is always given as an array of predicates. This top-level array is implicitly treated as a logical `and`, where `args` MUST pass validation of every top-level predicate. +Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare on a non-number MUST return false and MUST NOT throw an exception. -Policies are structured as trees. With the exception of subtrees under `some`, `or`, and `not`, every leaf MUST evaluate to `true`. `some`, `or`, and `not` must +### Glob Matching + +| Operator | Argument(s) | Example | +|----------|---------------------|---------------------------------------| +| `match` | `Selector, Pattern` | `["==", ".email", "*@*.example.com"]` | + +Glob patterns MUST only include a specicial character: `*` ("wildcard"). There is no single character matcher. `*` literals MUST be escaped (`"\*"`). Attempting to match on a non-string MUST return false and MUST NOT throw an exception. + +The wildcard represents zero-or-more characters. The following string literals MUST pass validation for the pattern `"Alice\*, Bob*, Carol.`: + +* `"Alice*, Bob, Carol."` +* `"Alice*, Bob, Dan, Erin, Carol."` +* `"Alice*, Bob*, Carol."` + +The following MUST NOT pass validation for that same pattern: + +* `"Alice*, Bob, Carol"` (missing the final `.`) +* `"Alice*, Bob*, Carol!"` (final `.` MUST NOT be treated as a wildcard) +* `"Alice, Bob, Carol."` (missing the `*` after `Alice`) +* `"Alice Cooper, Bob, Carol."` (the `*` after `Alice` is an escaped literal in the pattern) +* `" Alice*, Bob, Carol. "` (whitespace in the pattern is significant) + +### Connectives + +Connectives add context to their enclosed statement(s). + +| Operator | Argument(s) | Example | +|----------|---------------|--------------------------------------------| +| `not` | `Statement` | `["not", [">", ".a", 1]]` | +| `and` | `[Statement]` | `["and", [[">", ".a", 1], [">", ".b", 2]]` | +| `or` | `[Statement]` | `["or", [[">", ".a", 1], [">", ".b", 2]]` | + +`not` MUST invert the truth value of the inner statement. For example, if `["==", ".a", 1]` were false (`.a` is not 1), then `["not", ["==", ".a", 1]]` would be true. + +`and` MUST take an arbitrarily long array of statements, and require that every inner statement be true. An empty array MUST be treated as vacuously true. + +`or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as vacuously true. + +### Quantification + +When a selector resolves to a collection (an array or map), quantifiers provide a way to extend `and` and `or` to their contents. Attempting to quantify over a non-collection MUST return false and MUST NOT throw an exception. + +Quantifying over an array is straightforward: it MUST apply the inner statement to each array value. Quantifying over a map MUST extract the values (discarding the keys), and then MUST proceed onthe values the same as if it were an array. + +| Operator | Argument(s) | Example | +|----------|-------------------------|----------------------------------| +| `every` | `Selector, [Statement]` | `["every", ".a" [">", ".b", 1]]` | +| `some` | `Selector, [Statement]` | `["some", ".a" [">", ".b", 1]]` | + +`every` extends `and` over collections. `some` extends `or` over collections. For example: + +``` js +const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]} +const statement = ["every", ".a", [">", ".b", 0]] + +// Reduction +["every", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], [">", ".b", 0]] + +["and", [ + [ + [">", 1, 0], + [">", 2, 0], + [">", null, 0] + ] +] + +["and", + [ + true, + true, + false // <- + ] +] + +false // ❌ +``` + +``` js +const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]} +const statement = ["some", ".a", ["==", ".b", 2]] + +// Reduction +["some", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], ["==", ".b", 2]] + +["or", + [ + ["==", 1, 2], + ["==", 2, 2], + ["==", null, 2] + ] +] + +["or", + [ + false, + true, + false + ] +] + +true // ✅ +``` ### Selectors @@ -287,21 +400,40 @@ null +### Selector Taxonomy + +jq is a much larger language, and includes things like pipes, arithmatic, regexes, assignment, recursive descent, and so on. The UCAN policy language MUST only include the following features: + +| Selector Name | Examples | Notes | +|---------------------------|----------------------------|-------------------------------------------------------------------------------------------------| +| Identity | `.` | Take the entire argument | +| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | +| Unambiguous field name | `["."]` | Select in a map by arbitrary key | +| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | +| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | +| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | +| Try | `.foo?`, `.?`, `["nope"]?` | Returns `null` on what would otherwise fail | + +Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. + +The try operator is idempotent, and repeated tries (`.foo???`) MUST be allowed. + ### Validation -Validation +Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. + +If a selector cannot be resolved (there is no value at that path), -Being tree structured, evaluation MAY proceed in any order. Here is one step-by-step example: +#### Example Evaluation -
-Example Context +Below is a step-by-step evaluation example: ``` js { // Invocation "cmd": "/msg/send", "args": { "from": "alice@example.com", - "to": ["bob@example.com", "carol@elsewhere.example.com"], + "to": ["bob@example.com", "carol@not.example.com"], "title": "Coffee", "body": "Still on for coffee" }, @@ -318,130 +450,47 @@ Being tree structured, evaluation MAY proceed in any order. Here is one step-by- } ``` -
- -
-Code Example - ``` js -// Extract policy -[ +[ // Extract policy ["==", ".from", "alice@example.com"], ["some", ".to", ["match", ".", "*@example.com"]] ] -// Resolve selectors -[ +[ // Resolve selectors ["==", "alice@example.com", "alice@example.com"], ["some", ["bob@example.com", "carol@elsewhere.example.com"], ["match", ".", "*@example.com"]] ] -// Expand quantifier -[ +[ // Expand quantifier ["==", "alice@example.com", "alice@example.com"], ["or", [ ["match", "bob@example.com", "*@example.com"] - ["match", "carol@elsewhere.example.com", "*@example.com"]]] + ["match", "carol@elsewhere.example.com", "*@example.com"]] + ] ] -// Evaluate first clause -[ +[ // Evaluate first predicate true, ["or", [ ["match", "bob@example.com", "*@example.com"] ["match", "carol@elsewhere.example.com", "*@example.com"]]] ] -// Evaluate second clause's children -[ +[ // Evaluate second predicate's children true, ["or", [true, false]] ] -// Evaluate second clause -[true, true] -// Implicit top-level `and` -true -``` - -
- -
- -Graphical Example - -``` mermaid -flowchart BT - policy - eq["=="] --> policy - from[".from"] --> eq - alice["alice@example.com"] --> eq - - some --> policy - to[".to"] --> some - match --> some - this["."] --> match - pattern["*@example.com"] --> match -``` - -``` mermaid -flowchart BT - policy - eq["=="] --> policy - from["alice@example.com"] --> eq - alice["alice@example.com"] --> eq - - some --> policy - to["[]"] --> some - bob["bob@example.com"] --> to - carol["carol@not.example.com"] --> to - match --> some - this["."] --> match - pattern["*@example.com"] --> match -``` - -``` mermaid -flowchart BT - policy - eq["=="] --> policy - from["alice@example.com"] --> eq - alice["alice@example.com"] --> eq - - or --> policy - match_bob --> or - bob["bob@example.com"] --> match_bob - bob_pattern["*@example.com"] --> match_bob - - match_carol --> or - carol["carol@not.example.com"] --> match_carol - carol_pattern["*@example.com"] --> match_carol -``` - -``` mermaid -flowchart BT - policy - alice["true"] --> policy - - or --> policy - match_bob["true"] --> or - match_carol["false"] --> or -``` - -``` mermaid -flowchart BT - policy - alice["true"] --> policy - or["true"] --> policy -``` +[ // Evaluate second predicate + true, + true +] -``` mermaid -flowchart BT - true +// Evaluate top-level `and` +true ``` -
- Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use [Conditions]. Note that this also applies to arrays and objects. For example, the `to` array in this example is considered to be exact, so the Invocation fails validation in this case: @@ -449,7 +498,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i ``` js // Delegation { - "cmd": "/msg/send", + "cmd": "/email/send", "pol": [ ["==", ".from", "alice@example.com"], ["some", ".to", ["match", ".", "*@example.com"]] @@ -459,7 +508,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i // VALID Invocation { - "cmd": "/msg/send", + "cmd": "/email/send", "args": { "from": "alice@example.com", "to": ["bob@example.com", "carol@elsewhere.example.com"], @@ -471,10 +520,10 @@ Note that this also applies to arrays and objects. For example, the `to` array i // INVALID Invocation { - "cmd": "/msg/send", + "cmd": "/email/send", "args": { "from": "alice@example.com", - "to": ["carol@elsewhere.example.com"], // Missing `*@example.com` + "to": ["carol@elsewhere.example.com"], // No match for `*@example.com` "title": "Coffee", "body": "Still on for coffee" }, @@ -527,56 +576,9 @@ Condition semantics MUST be established by the Subject. They are openly extensib The above Delegation MUST be interpreted as "may send email from `alice@example.com` on Mondays as long as `bob@exmaple.com` is among the recipients" -The `if` field MUST take the following shape: `[{}]`. The array represents a logical `all` (chained `AND`s). To represent logical `OR`, issue another delegation with that attenuation. For instance, the following represents `{a: 1, b:2} AND {c: 3} AND {d: 4}`: - -``` js -[ - { // ┐ - a: 1, // ├─ Confition ─┐ - b: 2 // │ │ - }, // ┘ ├─ AND ─┐ - { // ┐ │ │ - c: 3 // ├─ Condition ─┘ │ - }, // ┘ ├─ AND - { // ┐ │ - d: 4 // ├─ Condition ─────────┘ - } // ┘ -] -``` - -Expressing Conditions in this standard way simplifies ad hoc extension at delegation time. As a concrete example, below a Condition is added to the constraints on the `to` field. - -``` js -// Original Condition -[ - { - "field": "to", - "includes": "bob@example.com" - } -] - -// Attenuated Conditions -[ - { // Same as above - "field": "to", - "includes": "bob@example.com" - }, - { // Also must send to carol@example.com - "field": "to", - "includes": "carol@example.com" - }, - { // Only send to @example.com addresses - "field": "to", - "elements": { - "match": "/.+@example.com/" - } - } -] -``` - # Validation -Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] prior to execution. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. +Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] _prior to execution_. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a Condition fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. From daea8ea780acd5455fc757550562c2e8e2cc6110 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 3 Jul 2024 11:54:18 -0700 Subject: [PATCH 105/134] Fix typos --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 569968e..f25dded 100644 --- a/README.md +++ b/README.md @@ -163,16 +163,18 @@ match = "['match', " selector ", " pattern "]" ; String wildcard matching ;; SELECTORS -selector = "." ; Identity - / *(".") 1*(subselector) ; Nested subselectors +selector = "." ; Identity + / 1*(subselector) ; Nested subselectors -subselector = "." 1*utf8 ; Dotted field selector - / "['" string "']" ; Explicit field selector - / "[" integer "]" ; Index selector +subselector = "." CHAR string ; Dotted field selector + / *1(".") "['" string "']" ; Explicit field selector + / *1(".") "[" integer "]" ; Index selector + / *1(".") "[]" ; Collection values // FIXME doble check code + / *1(".") "?" ; Try selector ;; SPECIAL LITERALS -pattern = *utf8 +pattern = string ; Reminder: IPLD string are UTF-8 number = integer / float ``` From 2533dab855ba45aca14caa2954bea4d70701cb79 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 3 Jul 2024 15:50:51 -0700 Subject: [PATCH 106/134] Cleaning up --- README.md | 185 ++++++++++++++++++++---------------------------------- 1 file changed, 67 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index f25dded..e95f7f3 100644 --- a/README.md +++ b/README.md @@ -40,17 +40,17 @@ The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0-rc.1`. The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|---------|------------------------------------------|----------|-------------------------------------------------------------| -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | -| `cmd` | `String` | Yes | The [Command] to eventually invoke | -| `pol` | `Policy` | Yes | [Policy] | -| `nonce` | `Bytes` | Yes | Nonce | -| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer \| nul` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| Field | Type | Required | Description | +|---------|-------------------------------------------|----------|-------------------------------------------------------------| +| `iss` | `DID` | Yes | Issuer DID (sender) | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `cmd` | `String` | Yes | The [Command] to eventually invoke | +| `pol` | `Policy` | Yes | [Policy] | +| `nonce` | `Bytes` | Yes | Nonce | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | # Capability @@ -88,7 +88,7 @@ For example: ``` js { - "sub": "did:web:example.com", + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // ... } ``` @@ -101,8 +101,7 @@ By default, the Resource of a capability is the Subject. This makes the delegati ``` js { - "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject & Resource - "cmd": "/crud/update", + "sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject // ... } ``` @@ -115,7 +114,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS "cmd": "/crud/create", "pol": [ ["==", ".url", "https://example.com/blog/"], // Resource managed by the Subject - ["==", ".status", "draft"] + // ... ], // ... } @@ -134,7 +133,7 @@ A Policy is an array of statements. Every statement MUST take the form `[operato Below is a formal syntax for the UCAN Policy Language given in [ABNF] (for DAG-JSON): ``` abnf -policy = "[" *1(statement *(", " statement)) "]" +policy = "[" *1(statement *("," statement)) "]" statement = equality / inequality / match @@ -143,38 +142,36 @@ statement = equality ;; STRUCTURAL -connective = "['not', " statement "]" ; Negation - / "['and', [" statement *(", " statement) "]]" ; Conjuction - / "['or', [" statement *(", " statement) "]]" ; Disjunction +connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Negation + / "[" DQUOTE "and" DQUOTE ",[" statement *("," statement) "]]" ; Conjuction + / "[" DQUOTE "or" DQUOTE ",[" statement *("," statement) "]]" ; Disjunction -quanitifier = "['every', " selector ", " policy "]" ; Universal - / "['some', " selector ", " policy "]" ; Existential +quanitifier = "[" DQUOTE "every" DQUOTE "," selector "," policy "]" ; Universal + / "[" DQUOTE "some" DQUOTE "," selector "," policy "]" ; Existential ;; COMPARISONS -// FIXME DQUOTE -equality = "['==', " selector ", " ipld "]" ; Equality on IPLD literals -inequality = "['>', " selector ", " number "]" ; Numeric greater-than - / "['>=', " selector ", " number "]" ; Numeric greater-than-or-equal - / "['<', " selector ", " number "]" ; Numeric lesser-than - / "['<=', " selector ", " number "]" ; Numeric lesser-than-or-equal +equality = "[" DQUOTE "==" DQUOTE "," selector "," ipld "]" ; Equality on IPLD literals +inequality = "[" DQUOTE ">" DQUOTE "," selector "," number "]" ; Numeric greater-than + / "[" DQUOTE ">=" DQUOTE "," selector "," number "]" ; Numeric greater-than-or-equal + / "[" DQUOTE "<" DQUOTE "," selector "," number "]" ; Numeric lesser-than + / "[" DQUOTE "<=" DQUOTE "," selector "," number "]" ; Numeric lesser-than-or-equal -match = "['match', " selector ", " pattern "]" ; String wildcard matching +match = "[" DQUOTE "match" DQUOTE "," selector "," pattern "]" ; String wildcard matching ;; SELECTORS -selector = "." ; Identity - / 1*(subselector) ; Nested subselectors +selector = DQUOTE "." DQUOTE ; Identity + / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with optional "try" -subselector = "." CHAR string ; Dotted field selector - / *1(".") "['" string "']" ; Explicit field selector - / *1(".") "[" integer "]" ; Index selector - / *1(".") "[]" ; Collection values // FIXME doble check code - / *1(".") "?" ; Try selector +subselector = "." CHAR string ; Dotted field selector + / *1(".") "[\" DQUOTE string "\" DQUOTE "]" ; Explicit field selector + / *1(".") "[" integer "]" ; Index selector + / *1(".") "[]" ; Collection values // FIXME doble check code ;; SPECIAL LITERALS -pattern = string ; Reminder: IPLD string are UTF-8 +pattern = DQUOTE string DQUOTE ; Reminder: IPLD strings are UTF-8 number = integer / float ``` @@ -183,27 +180,28 @@ number = integer / float | Operator | Argument(s) | Example | |----------|-------------------------------|----------------------------------| | `==` | `Selector, IPLD` | `["==", ".a", [1, 2, {"b": 3}]]` | -| `<` | `Selector, (float | integer)` | `["<", ".a", 1]` | -| `<=` | `Selector, (float | integer)` | `["<=", ".a", 1]` | -| `>` | `Selector, (float | integer)` | `[">", ".a", 1]` | -| `>=` | `Selector, (float | integer)` | `[">=", ".a", 1]` | +| `<` | `Selector, (integer | float)` | `["<", ".a", 1]` | +| `<=` | `Selector, (integer | float)` | `["<=", ".a", 1]` | +| `>` | `Selector, (integer | float)` | `[">", ".a", 1]` | +| `>=` | `Selector, (integer | float)` | `[">=", ".a", 1]` | Literal equality (`==`) MUST match the resolved selecor to entire IPLD argument. This is a "deep comparison". -Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare on a non-number MUST return false and MUST NOT throw an exception. +Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare a non-numeric type MUST return false and MUST NOT throw an exception. ### Glob Matching -| Operator | Argument(s) | Example | -|----------|---------------------|---------------------------------------| -| `match` | `Selector, Pattern` | `["==", ".email", "*@*.example.com"]` | +| Operator | Argument(s) | Example | +|----------|---------------------|-------------------------------------| +| `match` | `Selector, Pattern` | `["==", ".email", "*@example.com"]` | -Glob patterns MUST only include a specicial character: `*` ("wildcard"). There is no single character matcher. `*` literals MUST be escaped (`"\*"`). Attempting to match on a non-string MUST return false and MUST NOT throw an exception. +Glob patterns MUST only include one specicial character: `*` ("wildcard"). There is no single character matcher. As many `*`s as desired MAY be used. Non-wildcard `*`-literals MUST be escaped (`"\*"`). Attempting to match on a non-string MUST return false and MUST NOT throw an exception. The wildcard represents zero-or-more characters. The following string literals MUST pass validation for the pattern `"Alice\*, Bob*, Carol.`: * `"Alice*, Bob, Carol."` * `"Alice*, Bob, Dan, Erin, Carol."` +* `"Alice*, Bob , Carol."` * `"Alice*, Bob*, Carol."` The following MUST NOT pass validation for that same pattern: @@ -295,6 +293,16 @@ const statement = ["some", ".a", ["==", ".b", 2]] true // ✅ ``` +#### Nested Quantification + +Quantified statements MAY be nested. For example, the below states that someone with the email `fraud@example.com` is required to be among the receipts of every newsletter. + +``` js +["every", ".newsletters", + ["some", ".recipients", + ["==", ".email", "fraud@example.com"]]] +``` + ### Selectors Selector syntax is closely based on a subset of [jq]. They operate on an [Invocation]'s `args` object. @@ -402,9 +410,9 @@ null -### Selector Taxonomy +### Taxonomy -jq is a much larger language, and includes things like pipes, arithmatic, regexes, assignment, recursive descent, and so on. The UCAN policy language MUST only include the following features: +The UCAN policy language MUST only include the following features: | Selector Name | Examples | Notes | |---------------------------|----------------------------|-------------------------------------------------------------------------------------------------| @@ -420,13 +428,13 @@ Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, The try operator is idempotent, and repeated tries (`.foo???`) MUST be allowed. +[jq] is a much larger language, and includes things like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN predicate language. + ### Validation Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. -If a selector cannot be resolved (there is no value at that path), - -#### Example Evaluation +If a selector cannot be resolved (there is no value at that path), the associated statement MUST return false, and MUST NOT throw an exception. Note that for consistent semantics, selecting a missing keys on a map MUST return `null` (but nested selectors without a try MUST then fail the predicate). Below is a step-by-step evaluation example: @@ -535,48 +543,9 @@ Note that this also applies to arrays and objects. For example, the `to` array i The intended logic is expressible with [Conditions]. -## Conditions - -// FIXME -The `cond` field MUST contain any additional conditions. This concept is sometimes called a "caveat". Conditions constrain the capability in two ways: - -- Syntactic constraints on [Arguments] (length, regex, inclusion) -- Environmental / contextual conditions (day of week) - -Condition semantics MUST be established by the Subject. They are openly extensible, but vocabularies may be reused across many Subjects. Conditions MUST be Understood by the [Executor] of the eventual [Invocation][UCAN Invocation]. Each Condition MUST be formatted as a map. - -``` js -// Delegation -{ - "sub": "did:web:example.com", - "cmd": "msg/send", - "args": { - "from": "alice@example.com" - } - "pol": ["or", - // no recipient can have a `@gmail.com` email address - ["every", ".to", ["not", ["match", ".", "*@competitor.example.com"]]], - - // or at least one `bcc` field must include @ourteam.example.com - ["some", ".bcc", ["match", ".", "*@ourteam.example.com"]] - ], - // ... -} +## Semantic Conditions -// Valid Invocation, if Monday -{ - "do": "msg/send", - "args": { - "from": "alice@example.com", - "to": ["bob@example.com", "carol@elsewhere.example.com"], // Matches criteria - "title": "Coffee", - "body": "Still on for coffee" - }, - // ... -} -``` - -The above Delegation MUST be interpreted as "may send email from `alice@example.com` on Mondays as long as `bob@exmaple.com` is among the recipients" +Other semantic conditions that are not expressable syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that infromation in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field). # Validation @@ -584,7 +553,13 @@ Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of Each capability has its own semantics, which needs to be interpretable by the [Executor]. Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a Condition fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored. -If _any_ of the following criteria are not met, the UCAN MUST be considered invalid: +If _any_ of the following criteria are not met, the UCAN Delegation MUST be considered invalid: + +1. [Time Bounds] +2. [Principal Alignment] +3. [Signature Validation] + +Additional constraints MAY be placed on Delegations by specs that use them (notably [UCAN Invocation]). ## Time Bounds @@ -667,32 +642,6 @@ flowchart RL prf --> Delegations ``` -### Recipient Validation - -An agent executing a capability MUST verify that the outermost `aud` field _matches its own DID._ The associated ability MUST NOT be performed if they do not match. Recipient validation is REQUIRED to prevent the misuse of UCANs in an unintended context. - -The following UCAN fragment would be valid to invoke as `did:key:zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV`. Any other agent MUST NOT accept this UCAN. For example, `did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp` MUST NOT run the ability associated with that capability. - -``` js -{ - "aud": "did:key:zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "iss": "did:key:zAKJP3f7BD6W4iWEQ9jwndVTCBq8ua2Utt8EEjJ6Vxsf", - // ... -} -``` - -A good litmus test for invocation validity by a invoking agent is to check if they would be able to create a valid delegation for that capability. - -## Condition Attenuation - -FIXME - -The Condition array MAY be empty (which is equivalent to saying "with no other conditions"). Delegations MUST otherwise only append more Conditions, and recapitulate the existing ones verbatim. Here are some abstract examples: - -FIXME - -Conditions MAY be presented in any order, but merely appending to the array is RECOMMENDED. - ## Signature Validation The [Signature] field MUST validate against the `iss` DID from the [Payload]. From ccec3c3005780355cae93836fcbe569ad840c86a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Wed, 3 Jul 2024 16:26:07 -0700 Subject: [PATCH 107/134] Cleaning up headers --- README.md | 79 ++++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index e95f7f3..9f63fec 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ A Policy is always given as an array of predicates. This top-level array is impl Policies are structured as trees. With the exception of subtrees under `some`, `or`, and `not`, every leaf MUST evaluate to `true`. `some`, `or`, and `not` must -A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for nergation which MUST take the form `["not", term]`. +A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for negation which MUST take the form `["not", statement]`. Below is a formal syntax for the UCAN Policy Language given in [ABNF] (for DAG-JSON): @@ -175,21 +175,21 @@ pattern = DQUOTE string DQUOTE ; Reminder: IPLD strings are UTF-8 number = integer / float ``` -### Comparisons +## Comparisons -| Operator | Argument(s) | Example | -|----------|-------------------------------|----------------------------------| -| `==` | `Selector, IPLD` | `["==", ".a", [1, 2, {"b": 3}]]` | -| `<` | `Selector, (integer | float)` | `["<", ".a", 1]` | -| `<=` | `Selector, (integer | float)` | `["<=", ".a", 1]` | -| `>` | `Selector, (integer | float)` | `[">", ".a", 1]` | -| `>=` | `Selector, (integer | float)` | `[">=", ".a", 1]` | +| Operator | Argument(s) | Example | +|----------|--------------------------------|----------------------------------| +| `==` | `Selector, IPLD` | `["==", ".a", [1, 2, {"b": 3}]]` | +| `<` | `Selector, (integer \| float)` | `["<", ".a", 1]` | +| `<=` | `Selector, (integer \| float)` | `["<=", ".a", 1]` | +| `>` | `Selector, (integer \| float)` | `[">", ".a", 1]` | +| `>=` | `Selector, (integer \| float)` | `[">=", ".a", 1]` | Literal equality (`==`) MUST match the resolved selecor to entire IPLD argument. This is a "deep comparison". Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare a non-numeric type MUST return false and MUST NOT throw an exception. -### Glob Matching +## Glob Matching | Operator | Argument(s) | Example | |----------|---------------------|-------------------------------------| @@ -212,7 +212,7 @@ The following MUST NOT pass validation for that same pattern: * `"Alice Cooper, Bob, Carol."` (the `*` after `Alice` is an escaped literal in the pattern) * `" Alice*, Bob, Carol. "` (whitespace in the pattern is significant) -### Connectives +## Connectives Connectives add context to their enclosed statement(s). @@ -228,7 +228,7 @@ Connectives add context to their enclosed statement(s). `or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as vacuously true. -### Quantification +## Quantification When a selector resolves to a collection (an array or map), quantifiers provide a way to extend `and` and `or` to their contents. Attempting to quantify over a non-collection MUST return false and MUST NOT throw an exception. @@ -260,7 +260,7 @@ const statement = ["every", ".a", [">", ".b", 0]] [ true, true, - false // <- + false // ⬅️ ] ] @@ -285,7 +285,7 @@ const statement = ["some", ".a", ["==", ".b", 2]] ["or", [ false, - true, + true, // ⬅️ false ] ] @@ -293,7 +293,7 @@ const statement = ["some", ".a", ["==", ".b", 2]] true // ✅ ``` -#### Nested Quantification +### Nested Quantification Quantified statements MAY be nested. For example, the below states that someone with the email `fraud@example.com` is required to be among the receipts of every newsletter. @@ -303,9 +303,27 @@ Quantified statements MAY be nested. For example, the below states that someone ["==", ".email", "fraud@example.com"]]] ``` -### Selectors +## Selectors + +Selector syntax is closely based on [jq]'s "filters". They operate on an [Invocation]'s `args` object. -Selector syntax is closely based on a subset of [jq]. They operate on an [Invocation]'s `args` object. +Selectors MUST only include the following features: + +| Selector Name | Examples | Notes | +|---------------------------|----------------------------|-------------------------------------------------------------------------------------------------| +| Identity | `.` | Take the entire argument | +| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | +| Unambiguous field name | `["."]`, `["$_*"]` | Select in a map by arbitrary key | +| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | +| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | +| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | +| Try | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | + +Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. + +The try operator is idempotent, and repeated tries (`.foo???`) MUST be treated as a single one. + +[jq] is a much larger language, and includes additional features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. For example, consider the following `args` from an `Invocation`: @@ -401,7 +419,6 @@ NOTE: You cannot add _any_ indentation to this table if you want ``` json -// FIXME double check with implementation null ``` @@ -410,27 +427,7 @@ null -### Taxonomy - -The UCAN policy language MUST only include the following features: - -| Selector Name | Examples | Notes | -|---------------------------|----------------------------|-------------------------------------------------------------------------------------------------| -| Identity | `.` | Take the entire argument | -| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | -| Unambiguous field name | `["."]` | Select in a map by arbitrary key | -| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | -| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | -| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | -| Try | `.foo?`, `.?`, `["nope"]?` | Returns `null` on what would otherwise fail | - -Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. - -The try operator is idempotent, and repeated tries (`.foo???`) MUST be allowed. - -[jq] is a much larger language, and includes things like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN predicate language. - -### Validation +## Validation Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. @@ -541,11 +538,9 @@ Note that this also applies to arrays and objects. For example, the `to` array i } ``` -The intended logic is expressible with [Conditions]. - ## Semantic Conditions -Other semantic conditions that are not expressable syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that infromation in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field). +Other semantic conditions that are not possible to fully express syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that infromation in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field). # Validation From 40af005f55e1c6f6083c063f07d8ef474fee935f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Thu, 4 Jul 2024 12:24:34 -0700 Subject: [PATCH 108/134] Clarify difference from jq --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f63fec..e470c0f 100644 --- a/README.md +++ b/README.md @@ -323,8 +323,6 @@ Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, The try operator is idempotent, and repeated tries (`.foo???`) MUST be treated as a single one. -[jq] is a much larger language, and includes additional features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. - For example, consider the following `args` from an `Invocation`: ``` json @@ -427,6 +425,12 @@ null +### Differences from jq + +[jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. + +jq produces streams of values, in contract to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. + ## Validation Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. From cc7fe93f4451ab54899a91637731f8e6029b357a Mon Sep 17 00:00:00 2001 From: Steve Moyer Date: Mon, 5 Aug 2024 18:53:01 -0400 Subject: [PATCH 109/134] fix(policy): add line terminator so ABNF compiles (#14) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e470c0f..52ebca2 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Ne / "[" DQUOTE "and" DQUOTE ",[" statement *("," statement) "]]" ; Conjuction / "[" DQUOTE "or" DQUOTE ",[" statement *("," statement) "]]" ; Disjunction -quanitifier = "[" DQUOTE "every" DQUOTE "," selector "," policy "]" ; Universal +quantifier = "[" DQUOTE "every" DQUOTE "," selector "," policy "]" ; Universal / "[" DQUOTE "some" DQUOTE "," selector "," policy "]" ; Existential ;; COMPARISONS @@ -429,7 +429,7 @@ null [jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. -jq produces streams of values, in contract to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. +jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. ## Validation From 46b20a7d98637ee78f62fd9fabd74757aedc0295 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 01:40:07 +0200 Subject: [PATCH 110/134] Update README.md Co-authored-by: Irakli Gozalishvili Signed-off-by: Brooklyn Zelenka --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52ebca2..dfe5c65 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Capabilities are the semantically-relevant claims of a delegation. They MUST be |--------|-----------|----------|--------------------------------------------------------------------------------------------------| | `sub` | `DID` | Yes | The [Subject] that this Capability is about | | `cmd` | `Command` | Yes | The [Command] of this Capability | -| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language] | +| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language][Policy] | Here is an illustrative example: From 0c6bd015b4deeca60fd527549324fcb50083528d Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 16:45:31 -0700 Subject: [PATCH 111/134] match -> like --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dfe5c65..8d7916f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Here is an illustrative example: "cmd": "/blog/post/create", "pol": [ ["==", ".status", "draft"], - ["every", ".reviewer", ["match", ".email", "*@example.com"]], + ["every", ".reviewer", ["like", ".email", "*@example.com"]], ["some", ".tags", ["or", ["==", ".", "news"], @@ -136,7 +136,7 @@ Below is a formal syntax for the UCAN Policy Language given in [ABNF] (for DAG-J policy = "[" *1(statement *("," statement)) "]" statement = equality / inequality - / match + / like / connective / quantifier @@ -157,7 +157,7 @@ inequality = "[" DQUOTE ">" DQUOTE "," selector "," number "]" ; Numeric great / "[" DQUOTE "<" DQUOTE "," selector "," number "]" ; Numeric lesser-than / "[" DQUOTE "<=" DQUOTE "," selector "," number "]" ; Numeric lesser-than-or-equal -match = "[" DQUOTE "match" DQUOTE "," selector "," pattern "]" ; String wildcard matching +like = "[" DQUOTE "like" DQUOTE "," selector "," pattern "]" ; String wildcard matching ;; SELECTORS @@ -191,9 +191,9 @@ Numeric inequalities MUST be agnostic to numeric type. In other words, the decim ## Glob Matching -| Operator | Argument(s) | Example | -|----------|---------------------|-------------------------------------| -| `match` | `Selector, Pattern` | `["==", ".email", "*@example.com"]` | +| Operator | Argument(s) | Example | +|----------|---------------------|---------------------------------------| +| `like` | `Selector, Pattern` | `["like", ".email", "*@example.com"]` | Glob patterns MUST only include one specicial character: `*` ("wildcard"). There is no single character matcher. As many `*`s as desired MAY be used. Non-wildcard `*`-literals MUST be escaped (`"\*"`). Attempting to match on a non-string MUST return false and MUST NOT throw an exception. @@ -455,7 +455,7 @@ Below is a step-by-step evaluation example: "cmd": "/msg", "pol": [ ["==", ".from", "alice@example.com"], - ["some", ".to", ["match", ".", "*@example.com"]] + ["some", ".to", ["like", ".", "*@example.com"]] ], // ... } @@ -464,27 +464,27 @@ Below is a step-by-step evaluation example: ``` js [ // Extract policy ["==", ".from", "alice@example.com"], - ["some", ".to", ["match", ".", "*@example.com"]] + ["some", ".to", ["like", ".", "*@example.com"]] ] [ // Resolve selectors ["==", "alice@example.com", "alice@example.com"], - ["some", ["bob@example.com", "carol@elsewhere.example.com"], ["match", ".", "*@example.com"]] + ["some", ["bob@example.com", "carol@elsewhere.example.com"], ["like", ".", "*@example.com"]] ] [ // Expand quantifier ["==", "alice@example.com", "alice@example.com"], ["or", [ - ["match", "bob@example.com", "*@example.com"] - ["match", "carol@elsewhere.example.com", "*@example.com"]] + ["like", "bob@example.com", "*@example.com"] + ["like", "carol@elsewhere.example.com", "*@example.com"]] ] ] [ // Evaluate first predicate true, ["or", [ - ["match", "bob@example.com", "*@example.com"] - ["match", "carol@elsewhere.example.com", "*@example.com"]]] + ["like", "bob@example.com", "*@example.com"] + ["like", "carol@elsewhere.example.com", "*@example.com"]]] ] [ // Evaluate second predicate's children @@ -512,7 +512,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i "cmd": "/email/send", "pol": [ ["==", ".from", "alice@example.com"], - ["some", ".to", ["match", ".", "*@example.com"]] + ["some", ".to", ["like", ".", "*@example.com"]] ] // ... } From a955465eb84bd453b5a17d100368cb3ae034dd3f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 17:18:39 -0700 Subject: [PATCH 112/134] Break out connectives & add examples --- README.md | 56 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8d7916f..c0d2407 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ ## Version 1.0.0-rc.1 ## Editors +[Editors]: #editors - [Brooklyn Zelenka], [Witchcraft Software] ## Authors +[Authors]: #authors - [Brooklyn Zelenka], [Witchcraft Software] - [Daniel Holmgren], [Bluesky] @@ -13,30 +15,37 @@ - [Philipp Krüger], [number zero] ## Dependencies +[Dependencies]: #dependencies - [UCAN] ## Language +[Language]: #language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. # Abstract +[Abstract]: #abstract This specification describes the representation and semantics for delegating attenuated authority between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and a minimal syntatically-driven policy langauge. # Introduction +[Introduction]: #introduction UCAN Delegation is a delegable certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation]s. Delegation provides a way to "transfer authority without transferring cryptographic keys". As an authorization system, it is more interested in "what can be done" than a list of "who can do what". For more on how Delegation fits into UCAN, please refer to the [high level spec][UCAN]. # [UCAN Envelope] Configuration +[UCAN Envelope Configuration]: #ucan-envelope-configuration ## Type Tag +[Type Tag]: #type-tag The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0-rc.1`. ## Delegation Payload +[Delegation Payload]: #delegation-payload The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. @@ -52,15 +61,18 @@ The Delegation payload MUST describe the authorization claims, who is involved, | `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | | `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. + # Capability +[Capability]: #capability Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -| Field | Type | Required | Description | -|--------|-----------|----------|--------------------------------------------------------------------------------------------------| -| `sub` | `DID` | Yes | The [Subject] that this Capability is about | -| `cmd` | `Command` | Yes | The [Command] of this Capability | -| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language][Policy] | +| Field | Type | Required | Description | +|--------|---------------|----------|--------------------------------------------------------------------------------------------------| +| `sub` | `DID \| null` | Yes | The [Subject] that this Capability is about | +| `cmd` | `Command` | Yes | The [Command] of this Capability | +| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language][Policy] | Here is an illustrative example: @@ -81,8 +93,9 @@ Here is an illustrative example: ``` ## Subject +[Subject]: #subject -The Subject MUST be the DID that initiated the delegation chain. +The Subject MUST be the DID that initiated the delegation chain, or an explicit `null`. For more on the `null`, please see the [Powerline] section. For example: @@ -94,6 +107,7 @@ For example: ``` ## Resource +[Resource]: #resource Unlike Subjects and Commands, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. @@ -121,6 +135,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS ``` # Policy Language +[Policy Language]: #policy-language UCAN Delegation uses predicate logic statements extended with [jq]-style selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. @@ -176,6 +191,7 @@ number = integer / float ``` ## Comparisons +[Comparisons]: #comparisons | Operator | Argument(s) | Example | |----------|--------------------------------|----------------------------------| @@ -190,6 +206,7 @@ Literal equality (`==`) MUST match the resolved selecor to entire IPLD argument. Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare a non-numeric type MUST return false and MUST NOT throw an exception. ## Glob Matching +[Glob Matching]: #glob-matching | Operator | Argument(s) | Example | |----------|---------------------|---------------------------------------| @@ -213,6 +230,7 @@ The following MUST NOT pass validation for that same pattern: * `" Alice*, Bob, Carol. "` (whitespace in the pattern is significant) ## Connectives +[Connectives]: #connectives Connectives add context to their enclosed statement(s). @@ -229,6 +247,7 @@ Connectives add context to their enclosed statement(s). `or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as vacuously true. ## Quantification +[Quantification]: #quantification When a selector resolves to a collection (an array or map), quantifiers provide a way to extend `and` and `or` to their contents. Attempting to quantify over a non-collection MUST return false and MUST NOT throw an exception. @@ -294,6 +313,7 @@ true // ✅ ``` ### Nested Quantification +[Nested Quantification]: #nested-quantification Quantified statements MAY be nested. For example, the below states that someone with the email `fraud@example.com` is required to be among the receipts of every newsletter. @@ -304,6 +324,7 @@ Quantified statements MAY be nested. For example, the below states that someone ``` ## Selectors +[Selectors]: #selectors Selector syntax is closely based on [jq]'s "filters". They operate on an [Invocation]'s `args` object. @@ -426,12 +447,14 @@ null ### Differences from jq +[Differences from jq]: #differences-from-jq [jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. ## Validation +[Validation]: #validation Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. @@ -543,10 +566,13 @@ Note that this also applies to arrays and objects. For example, the `to` array i ``` ## Semantic Conditions +[Semantics Conditions]: #semantic-conditions Other semantic conditions that are not possible to fully express syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that infromation in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field). +// FIXME duplicate header # Validation +[Validation]: #validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] _prior to execution_. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. @@ -561,6 +587,7 @@ If _any_ of the following criteria are not met, the UCAN Delegation MUST be cons Additional constraints MAY be placed on Delegations by specs that use them (notably [UCAN Invocation]). ## Time Bounds +[Time Bounds]: #time-bounds A UCAN's time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the "validity period." Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegation chain valid between the latest `nbf` and earliest `exp`. @@ -581,6 +608,7 @@ const ensureTime = (delegationChain, now) => { ``` ## Principal Alignment +[Principal Alignment]: #principal-alignment In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the Subject for each resource. @@ -642,10 +670,12 @@ flowchart RL ``` ## Signature Validation +[Signature Validation]: #signature-validation The [Signature] field MUST validate against the `iss` DID from the [Payload]. # Acknowledgments +[Acknowledgments]: #acknowledgments Thank you to [Brendan O'Brien] for real-world feedback, technical collaboration, and implementing the first Golang UCAN library. @@ -671,20 +701,6 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. - - -[^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. - - - -[Ability]: #ability -[Envelope]: #delegation-envelope -[Meta]: #meta -[Payload]: #delegation-payload -[Subject]: #subject -[Policy]: #policy-language -[Policy Language]: #policy-language - [Alan Karp]: https://github.com/alanhkarp From 5ecc144e58c232cfc4c3f4360618a27fab726ec9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 17:39:50 -0700 Subject: [PATCH 113/134] Reference powerline, make sub nullable --- README.md | 116 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 89 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c0d2407..ef5f671 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Delegation provides a way to "transfer authority without transferring cryptograp # [UCAN Envelope] Configuration [UCAN Envelope Configuration]: #ucan-envelope-configuration - + ## Type Tag [Type Tag]: #type-tag @@ -53,7 +53,7 @@ The Delegation payload MUST describe the authorization claims, who is involved, |---------|-------------------------------------------|----------|-------------------------------------------------------------| | `iss` | `DID` | Yes | Issuer DID (sender) | | `aud` | `DID` | Yes | Audience DID (receiver) | -| `sub` | `DID` | Yes | Principal that the chain is about (the [Subject]) | +| `sub` | `DID \| null` | Yes | Principal that the chain is about (the [Subject]) | | `cmd` | `String` | Yes | The [Command] to eventually invoke | | `pol` | `Policy` | Yes | [Policy] | | `nonce` | `Bytes` | Yes | Nonce | @@ -68,10 +68,10 @@ The Delegation payload MUST describe the authorization claims, who is involved, Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: -| Field | Type | Required | Description | -|--------|---------------|----------|--------------------------------------------------------------------------------------------------| -| `sub` | `DID \| null` | Yes | The [Subject] that this Capability is about | -| `cmd` | `Command` | Yes | The [Command] of this Capability | +| Field | Type | Required | Description | +|--------|---------------|----------|----------------------------------------------------------------------------------------------------------| +| `sub` | `DID \| null` | Yes | The [Subject] that this Capability is about | +| `cmd` | `Command` | Yes | The [Command] of this Capability | | `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language][Policy] | Here is an illustrative example: @@ -95,9 +95,7 @@ Here is an illustrative example: ## Subject [Subject]: #subject -The Subject MUST be the DID that initiated the delegation chain, or an explicit `null`. For more on the `null`, please see the [Powerline] section. - -For example: +The Subject MUST be the DID that initiated the delegation chain, or an explicit `null`. Declaring a DID is RECOMMENDED. For more on the `null`, please see the [Powerline] section. ``` js { @@ -157,7 +155,7 @@ statement = equality ;; STRUCTURAL -connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Negation +connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Negation / "[" DQUOTE "and" DQUOTE ",[" statement *("," statement) "]]" ; Conjuction / "[" DQUOTE "or" DQUOTE ",[" statement *("," statement) "]]" ; Disjunction @@ -236,15 +234,80 @@ Connectives add context to their enclosed statement(s). | Operator | Argument(s) | Example | |----------|---------------|--------------------------------------------| -| `not` | `Statement` | `["not", [">", ".a", 1]]` | | `and` | `[Statement]` | `["and", [[">", ".a", 1], [">", ".b", 2]]` | | `or` | `[Statement]` | `["or", [[">", ".a", 1], [">", ".b", 2]]` | +| `not` | `Statement` | `["not", [">", ".a", 1]]` | + +### And +[And]: #and + +`and` MUST take an arbitrarily long array of statements, and require that every inner statement be true. An empty array MUST be treated as true. + +```js +// Data +{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } + +["and", []] +// ☝️ true + +["and", [ + ["==", ".name", "Katie"], + [">=", ".age", 21] +]] +// ☝️ true + +["and", [ + ["==", ".name", "Katie"], + [">=", ".age", 21], + ["==", ".nationality", ["American"]] // ️👈 false +]] +// ☝️ false +``` + +### Or +[Or]: #or + +`or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as true. + +```js +// Data +{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } + +["or", []] +// ☝️ true + +["or", [ + ["==", ".name", "Katie"], // ️👈 true + [">", ".age", 45] +]] +// ☝️ true +``` + +### Not +[Not]: #not `not` MUST invert the truth value of the inner statement. For example, if `["==", ".a", 1]` were false (`.a` is not 1), then `["not", ["==", ".a", 1]]` would be true. -`and` MUST take an arbitrarily long array of statements, and require that every inner statement be true. An empty array MUST be treated as vacuously true. +```js +// Data +{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } + +["and", []] +// ☝️ true -`or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as vacuously true. +["and", [ + ["==", ".name", "Katie"], + [">=", ".age", 21] +]] +// ☝️ true + +["not", + ["and", [ // ️👈 false + ["==", ".name", "Katie"], + ["==", ".nationality", ["American"]] // ️👈 false +]] +// ☝️ true +``` ## Quantification [Quantification]: #quantification @@ -293,21 +356,17 @@ const statement = ["some", ".a", ["==", ".b", 2]] // Reduction ["some", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], ["==", ".b", 2]] -["or", - [ - ["==", 1, 2], - ["==", 2, 2], - ["==", null, 2] - ] -] +["or", [ + ["==", 1, 2], + ["==", 2, 2], + ["==", null, 2] +]] -["or", - [ - false, - true, // ⬅️ - false - ] -] +["or", [ + false, + true, // ⬅️ + false +]] true // ✅ ``` @@ -453,6 +512,9 @@ null jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. +## Powerlines +[Powerlines]: #powerlines + ## Validation [Validation]: #validation From 4a8c4352f8ed11e9553feb8ab96d70d358714d13 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:13:07 -0700 Subject: [PATCH 114/134] Add powerline section --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ef5f671..4b4ebe5 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,10 @@ The Subject MUST be the DID that initiated the delegation chain, or an explicit } ``` -## Resource +### Resource [Resource]: #resource -Unlike Subjects and Commands, Resources are semantic rather than syntactic. The Resource is the "what" that a capability describes. +Unlike [Subjects][Subject] and [Commands][Command], Resources are _semantic_ rather than syntactic. The Resource is the "what" that a capability describes. By default, the Resource of a capability is the Subject. This makes the delegation chain self-certifying. @@ -132,6 +132,52 @@ In the case where access to an [external resource] is delegated, the Subject MUS } ``` +### Powerline +[Powerline]: #powerline + +> [!WARNING] +> Just like `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very feature. Use with care. + +A "Powerline"[^powerbox] is a pattern for automatically delegating _all_ future delegations to another agent regardless of [Subject]. This is achieved by explicitly setting the [Subject] (`sub`) field to `null`. At [Validation] time, the [Subject] MUST be substituted for the directly prior Subject given in the delegation chain. + +[^powerbox]: For those familiar with design patterns for object capabilities, a "Powerline" is like a [Powerbox] but adapted for the partition-tolerant, static token context of UCAN. + +Powerline delegations MUST NOT be used as the root delegation to a resource. A priori there is no such thing as a `null` resource. + +A very common use case for Powerlines is providing a stable DID across multiple agents (e.g. representing a user with multiple devices). This enables the automatic sharing of authority across their devices without needing to share keys or set up a threshold scheme. It is also flexible, since a Powerline delegation MAY be [revoked][revocation]. + +``` mermaid +sequenceDiagram + autonumber + + participant Email Server + participant Alice Root + participant Alice's Phone + participant Alice's Tablet + participant Alice's Laptop + + Alice Root ->> Alice's Phone: Delegate {sub: null, cmd: "/"} + Alice Root ->> Alice's Tablet: Delegate {sub: null, cmd: "/"} + Alice Root ->> Alice's Laptop: Delegate {sub: null, cmd: "/"} + + Email Server ->> Alice Root: Delegate {sub: "did:example:email", cmd: "/msg/send"} + + Alice's Tablet -->> Email Server: INVOKE! {sub: "did:example:email", cmd: "/msg/send", proofs: [❹,❷]} +``` + +Powerlines MAY include other restrictions, such as [time bounds][Time Bounds], [Commands][Command], and [Policies][Policy]. For example, the ability to automatically redelegate read-only access to arbitrary resources could be expressed as: + +``` js +{ + "iss": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", + "aud": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", + "sub": null, // ⚡Powerline + "cmd": "/crud/read", + "pol": [], + // ... +} +``` + # Policy Language [Policy Language]: #policy-language @@ -143,13 +189,13 @@ Policies are structured as trees. With the exception of subtrees under `some`, ` A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for negation which MUST take the form `["not", statement]`. -Below is a formal syntax for the UCAN Policy Language given in [ABNF] (for DAG-JSON): +Below is a formal syntax for the UCAN Policy Language given in [ABNF] (representing IPLD as [DAG-JSON]): ``` abnf policy = "[" *1(statement *("," statement)) "]" statement = equality / inequality - / like + / wildcard / connective / quantifier @@ -170,7 +216,7 @@ inequality = "[" DQUOTE ">" DQUOTE "," selector "," number "]" ; Numeric great / "[" DQUOTE "<" DQUOTE "," selector "," number "]" ; Numeric lesser-than / "[" DQUOTE "<=" DQUOTE "," selector "," number "]" ; Numeric lesser-than-or-equal -like = "[" DQUOTE "like" DQUOTE "," selector "," pattern "]" ; String wildcard matching +wildcard = "[" DQUOTE "like" DQUOTE "," selector "," pattern "]" ; String wildcard matching ;; SELECTORS @@ -512,9 +558,6 @@ null jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. -## Powerlines -[Powerlines]: #powerlines - ## Validation [Validation]: #validation @@ -765,6 +808,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[ABNF]: https://datatracker.ietf.org/doc/html/rfc5234 [Alan Karp]: https://github.com/alanhkarp [Benjamin Goering]: https://github.com/gobengo [Blaine Cook]: https://github.com/blaine @@ -777,6 +821,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Christopher Joel]: https://github.com/cdata [Command]: https://github.com/ucan-wg/spec#33-command [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ +[DAG-JSON]: https://ipld.io/specs/codecs/dag-json/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology [DID]: https://www.w3.org/TR/did-core/ [Dan Finlay]: https://github.com/danfinlay @@ -800,6 +845,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [OCapN]: https://github.com/ocapn/ [Philipp Krüger]: https://github.com/matheus23 [PoLA]: https://en.wikipedia.org/wiki/Principle_of_least_privilege +[Powerbox]: https://sandstorm.io/how-it-works#powerbox [Protocol Labs]: https://protocol.ai/ [RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339 [RFC 8037]: https://www.rfc-editor.org/rfc/rfc8037 From 5311dcefe0dc0c8b33f88f9281ef0e5f8555cc6a Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:14:27 -0700 Subject: [PATCH 115/134] Add missing word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b4ebe5..036c790 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS [Powerline]: #powerline > [!WARNING] -> Just like `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very feature. Use with care. +> Just like `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very powerful feature. Use with care. A "Powerline"[^powerbox] is a pattern for automatically delegating _all_ future delegations to another agent regardless of [Subject]. This is achieved by explicitly setting the [Subject] (`sub`) field to `null`. At [Validation] time, the [Subject] MUST be substituted for the directly prior Subject given in the delegation chain. From 0f453dd39c8d9a024bad635df6252124f3c33262 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:15:41 -0700 Subject: [PATCH 116/134] Clearer pointer in example --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 036c790..c6f034d 100644 --- a/README.md +++ b/README.md @@ -165,13 +165,13 @@ sequenceDiagram Alice's Tablet -->> Email Server: INVOKE! {sub: "did:example:email", cmd: "/msg/send", proofs: [❹,❷]} ``` -Powerlines MAY include other restrictions, such as [time bounds][Time Bounds], [Commands][Command], and [Policies][Policy]. For example, the ability to automatically redelegate read-only access to arbitrary resources could be expressed as: +Powerlines MAY include other restrictions, such as [time bounds][Time Bounds], [Commands][Command], and [Policies][Policy]. For example, the ability to automatically redelegate read-only access to arbitrary CRUD resources could be expressed as: ``` js { "iss": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", "aud": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme", - "sub": null, // ⚡Powerline + "sub": null, // 👈 ⚡ Powerline "cmd": "/crud/read", "pol": [], // ... From 4acded94234b144c42fd5939f47aee4122412630 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:21:16 -0700 Subject: [PATCH 117/134] Don't mind me, just fighting with GH's markdown renderer --- README.md | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c6f034d..98013d0 100644 --- a/README.md +++ b/README.md @@ -291,23 +291,23 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } +{ "name": "Katie", "age": 35, nationalities: ["Canadian", "South African"] } ["and", []] -// ☝️ true +// ⬆️ true ["and", [ ["==", ".name", "Katie"], [">=", ".age", 21] ]] -// ☝️ true +// ⬆️ true ["and", [ ["==", ".name", "Katie"], [">=", ".age", 21], - ["==", ".nationality", ["American"]] // ️👈 false + ["==", ".nationalities", ["American"]] // ️⬅️ false ]] -// ☝️ false +// ⬆️ false ``` ### Or @@ -317,16 +317,16 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } +{ "name": "Katie", "age": 35, nationalities: ["Canadian", "South African"] } ["or", []] -// ☝️ true +// ⬆️ true ["or", [ - ["==", ".name", "Katie"], // ️👈 true + ["==", ".name", "Katie"], // ⬅️ true [">", ".age", 45] ]] -// ☝️ true +// ⬆️ true ``` ### Not @@ -336,23 +336,14 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", "age": 35, nationality: ["Canadian", "South African"] } - -["and", []] -// ☝️ true - -["and", [ - ["==", ".name", "Katie"], - [">=", ".age", 21] -]] -// ☝️ true +{ "name": "Katie", nationalities: ["Canadian", "South African"] } ["not", - ["and", [ // ️👈 false + ["and", [ ["==", ".name", "Katie"], - ["==", ".nationality", ["American"]] // ️👈 false + ["==", ".nationalities", ["American"]] // ⬅️ false ]] -// ☝️ true +// ⬆️ true ``` ## Quantification From 4c404232e07898517470253fd40c9eb7af59fc05 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:25:38 -0700 Subject: [PATCH 118/134] Wordsmith powerlines --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 98013d0..6793edf 100644 --- a/README.md +++ b/README.md @@ -136,13 +136,13 @@ In the case where access to an [external resource] is delegated, the Subject MUS [Powerline]: #powerline > [!WARNING] -> Just like `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very powerful feature. Use with care. +> Similar to `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very powerful feature. Use with care. -A "Powerline"[^powerbox] is a pattern for automatically delegating _all_ future delegations to another agent regardless of [Subject]. This is achieved by explicitly setting the [Subject] (`sub`) field to `null`. At [Validation] time, the [Subject] MUST be substituted for the directly prior Subject given in the delegation chain. +A "Powerline"[^powerbox] is a pattern for automatically delegating _all_ future delegations to another agent regardless of [Subject]. This is achieved by explicitly setting the [Subject] (`sub`) field to `null`. At [Validation] time, the [Subject] MUST be substituted for the directly prior Subject given in the delegation chain. All other fields MUST continue to validate as normal (e.g. [principal alignment][Principal Alignment], [time bounds][Time Bounds], and so on). [^powerbox]: For those familiar with design patterns for object capabilities, a "Powerline" is like a [Powerbox] but adapted for the partition-tolerant, static token context of UCAN. -Powerline delegations MUST NOT be used as the root delegation to a resource. A priori there is no such thing as a `null` resource. +Powerline delegations MUST NOT be used as the root delegation to a resource. A priori there is no such thing as a `null` subject a prior. A very common use case for Powerlines is providing a stable DID across multiple agents (e.g. representing a user with multiple devices). This enables the automatic sharing of authority across their devices without needing to share keys or set up a threshold scheme. It is also flexible, since a Powerline delegation MAY be [revoked][revocation]. From 0b723ff1a500605484aa720eccd75970115440f0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 18:26:03 -0700 Subject: [PATCH 119/134] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6793edf..c36f58b 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ In the case where access to an [external resource] is delegated, the Subject MUS [Powerline]: #powerline > [!WARNING] -> Similar to `cmd: "/"` and `pol: []`, this feature (`sub: null`) is a very powerful feature. Use with care. +> Similar to `cmd: "/"` and `pol: []`, this feature (`sub: null`) is very powerful. Use with care. A "Powerline"[^powerbox] is a pattern for automatically delegating _all_ future delegations to another agent regardless of [Subject]. This is achieved by explicitly setting the [Subject] (`sub`) field to `null`. At [Validation] time, the [Subject] MUST be substituted for the directly prior Subject given in the delegation chain. All other fields MUST continue to validate as normal (e.g. [principal alignment][Principal Alignment], [time bounds][Time Bounds], and so on). From 846779b61f0f749d45728048a44c0f281c2fcb94 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 21:50:13 -0700 Subject: [PATCH 120/134] How to select into bytes --- README.md | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c36f58b..eef2aa1 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The Delegation payload MUST describe the authorization claims, who is involved, # Capability [Capability]: #capability -Capabilities are the semantically-relevant claims of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: +A capability is the semantically-relevant claim of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: | Field | Type | Required | Description | |--------|---------------|----------|----------------------------------------------------------------------------------------------------------| @@ -291,7 +291,7 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", "age": 35, nationalities: ["Canadian", "South African"] } +{ name: "Katie", age: 35, nationalities: ["Canadian", "South African"] } ["and", []] // ⬆️ true @@ -317,7 +317,7 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", "age": 35, nationalities: ["Canadian", "South African"] } +{ name: "Katie", age: 35, nationalities: ["Canadian", "South African"] } ["or", []] // ⬆️ true @@ -336,7 +336,7 @@ Connectives add context to their enclosed statement(s). ```js // Data -{ "name": "Katie", nationalities: ["Canadian", "South African"] } +{ name: "Katie", nationalities: ["Canadian", "South African"] } ["not", ["and", [ @@ -364,24 +364,21 @@ Quantifying over an array is straightforward: it MUST apply the inner statement const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]} const statement = ["every", ".a", [">", ".b", 0]] -// Reduction +// Outer Selector Substitution ["every", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], [">", ".b", 0]] +// Predicate Reduction ["and", [ - [ - [">", 1, 0], - [">", 2, 0], - [">", null, 0] - ] -] + [">", 1, 0], + [">", 2, 0], + [">", null, 0] +]] -["and", - [ - true, - true, - false // ⬅️ - ] -] +["and", [ + true, + true, + false // ⬅️ +]] false // ❌ ``` @@ -542,6 +539,22 @@ null +### Selecting on Bytes + +Bytes MAY be selected into. When doing so, they MUST be treated as a byte array (`[u8]`), and MUST NOT be treated as a Base64 string or any other representation. + +``` js +// DAG-JSON +{ "/": { "bytes": "1qnBjPjE" } } + +// Hexadecimal +0xd6 0xa9 0xc1 0x8c 0xf8 0xc4 + +// Selector +".[3]" +// ⬆️ 0x8c = 140 +``` + ### Differences from jq [Differences from jq]: #differences-from-jq From f2d9bd7af947d304e2d883a5fbee7dba725a5bbf Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 21:59:03 -0700 Subject: [PATCH 121/134] Add slices to table --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eef2aa1..c1b831a 100644 --- a/README.md +++ b/README.md @@ -423,15 +423,16 @@ Selector syntax is closely based on [jq]'s "filters". They operate on an [Invoca Selectors MUST only include the following features: -| Selector Name | Examples | Notes | -|---------------------------|----------------------------|-------------------------------------------------------------------------------------------------| -| Identity | `.` | Take the entire argument | -| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | -| Unambiguous field name | `["."]`, `["$_*"]` | Select in a map by arbitrary key | -| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | -| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | -| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | -| Try | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | +| Selector Name | Examples | Notes | +|---------------------------|-------------------------------------|-------------------------------------------------------------------------------------------------| +| Identity | `.` | Take the entire argument | +| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | +| Unambiguous field name | `["."]`, `["$_*"]` | Select in a map by arbitrary key | +| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | +| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | +| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | +| Collection slices | `[7:11]`, `[2:]`, `[:42]`, `[0:-2]` | The range of elements by their indices. | +| Optional | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. From 12e4a9cf74a447ca904514659b10c0d632039dc8 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:01:29 -0700 Subject: [PATCH 122/134] Rename `try` to `optional` everywhere --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c1b831a..ce0b612 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ wildcard = "[" DQUOTE "like" DQUOTE "," selector "," pattern "]" ; String wil ;; SELECTORS selector = DQUOTE "." DQUOTE ; Identity - / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with optional "try" + / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with possible optionals subselector = "." CHAR string ; Dotted field selector / *1(".") "[\" DQUOTE string "\" DQUOTE "]" ; Explicit field selector @@ -436,7 +436,7 @@ Selectors MUST only include the following features: Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. -The try operator is idempotent, and repeated tries (`.foo???`) MUST be treated as a single one. +The optional operator is idempotent, and repeated optionals (`.foo???`) MUST be treated as a single one. For example, consider the following `args` from an `Invocation`: @@ -561,14 +561,14 @@ Bytes MAY be selected into. When doing so, they MUST be treated as a byte array [jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. -jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the try (`?`) operator: UCAN's `try` selector operator MUST return `null` for the failure case. +jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the optional (`?`) operator: UCAN's optional selector operator MUST return `null` for the failure case. ## Validation [Validation]: #validation Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order. -If a selector cannot be resolved (there is no value at that path), the associated statement MUST return false, and MUST NOT throw an exception. Note that for consistent semantics, selecting a missing keys on a map MUST return `null` (but nested selectors without a try MUST then fail the predicate). +If a selector cannot be resolved (there is no value at that path), the associated statement MUST return false, and MUST NOT throw an exception. Note that for consistent semantics, selecting a missing keys on a map MUST return `null` (but nested selectors without an optional MUST then fail the predicate). Below is a step-by-step evaluation example: From f4316f402aa7bf47406c352c5296f2fa1c22ccad Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:10:25 -0700 Subject: [PATCH 123/134] Thanks Steve Moyer!! --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ce0b612..7f03a03 100644 --- a/README.md +++ b/README.md @@ -801,6 +801,8 @@ Many thanks to [Christine Lemmer-Webber] for her handwritten(!) feedback on the Thanks to [Benjamin Goering] for the many community threads and connections to [W3C] standards. +Thanks to [Steve Moyer] for his detailed feedback on the selector design and thoughts on ANBF generation. + Thanks to [Juan Caballero] for the numerous questions, clarifications, and general advice on putting together a comprehensible spec. Thank you [Dan Finlay] for being sufficiently passionate about [OCAP] that we realized that capability systems had a real chance of adoption in an ACL-dominated world. @@ -813,6 +815,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to +[Steve Moyer]: https://github.com/smoyer64 [ABNF]: https://datatracker.ietf.org/doc/html/rfc5234 [Alan Karp]: https://github.com/alanhkarp [Benjamin Goering]: https://github.com/gobengo From fbb7827b168b39094ab6e2356a864b60a466a03f Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:41:18 -0700 Subject: [PATCH 124/134] Add missing commad section per gozala --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7f03a03..3756b9f 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,18 @@ Powerlines MAY include other restrictions, such as [time bounds][Time Bounds], [ } ``` +## Command +[Command]: #command + +The [Command][High Level Command] MUST be a `/` delimited path describing set of commands delegated. +Delegation covers exact [Command] specified and all the commands described by a paths nested under that specified command. + +> [!NOTE] +> The command path syntax is designed to support forward compatible protocol extensions. Backwards-compatibl️️️️️️️️️️e capabilities MAY be introduced as command subpaths. + +> [!WARNINIG] +> By definition `"/"` implies all of the commands available on a resource, and SHOULD be used with great care. + # Policy Language [Policy Language]: #policy-language @@ -801,7 +813,7 @@ Many thanks to [Christine Lemmer-Webber] for her handwritten(!) feedback on the Thanks to [Benjamin Goering] for the many community threads and connections to [W3C] standards. -Thanks to [Steve Moyer] for his detailed feedback on the selector design and thoughts on ANBF generation. +Thanks to [Steve Moyer] for his detailed feedback on the selector design and thoughts on ANBF codegen. Thanks to [Juan Caballero] for the numerous questions, clarifications, and general advice on putting together a comprehensible spec. @@ -815,7 +827,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to -[Steve Moyer]: https://github.com/smoyer64 [ABNF]: https://datatracker.ietf.org/doc/html/rfc5234 [Alan Karp]: https://github.com/alanhkarp [Benjamin Goering]: https://github.com/gobengo @@ -827,7 +838,6 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata -[Command]: https://github.com/ucan-wg/spec#33-command [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DAG-JSON]: https://ipld.io/specs/codecs/dag-json/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology @@ -838,6 +848,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [EdDSA]: https://en.wikipedia.org/wiki/EdDSA [Executor]: https://github.com/ucan-wg/spec#31-roles [Fission]: https://fission.codes +[High Level Command]: https://github.com/ucan-wg/spec#33-command [Hugo Dias]: https://github.com/hugomrdias [IEEE-754]: https://ieeexplore.ieee.org/document/8766229 [IPLD]: https://ipld.io/ @@ -863,6 +874,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [SHA2-256]: https://github.com/multiformats/multicodec/blob/master/table.csv#L9 [SPKI/SDSI]: https://datatracker.ietf.org/wg/spki/about/ [SPKI]: https://theworld.com/~cme/html/spki.html +[Steve Moyer]: https://github.com/smoyer64 [Steven Vandevelde]: https://github.com/icidasset [UCAN Envelope]: https://github.com/ucan-wg/spec/blob/high-level/README.md#envelope [UCAN Invocation]: https://github.com/ucan-wg/invocation From 58d78e15f469597b11e219527ab461bfc0b699d9 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:43:13 -0700 Subject: [PATCH 125/134] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3756b9f..2d8ab6b 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Delegation covers exact [Command] specified and all the commands described by a > [!NOTE] > The command path syntax is designed to support forward compatible protocol extensions. Backwards-compatibl️️️️️️️️️️e capabilities MAY be introduced as command subpaths. -> [!WARNINIG] +> [!WARNING] > By definition `"/"` implies all of the commands available on a resource, and SHOULD be used with great care. # Policy Language From 66513e6a061b91a758fe0ec9807d14f9998372a0 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:44:35 -0700 Subject: [PATCH 126/134] Fix broken link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d8ab6b..d5ccc04 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,8 @@ Delegation covers exact [Command] specified and all the commands described by a > [!WARNING] > By definition `"/"` implies all of the commands available on a resource, and SHOULD be used with great care. -# Policy Language -[Policy Language]: #policy-language +# Policy +[Policy]: #policy UCAN Delegation uses predicate logic statements extended with [jq]-style selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. From c7a92a8b745cf4c60c09ff220726edacbf0e9fb4 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 5 Aug 2024 22:47:18 -0700 Subject: [PATCH 127/134] Require leading dot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5ccc04..f450a83 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ Selectors MUST only include the following features: | Collection slices | `[7:11]`, `[2:]`, `[:42]`, `[0:-2]` | The range of elements by their indices. | | Optional | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | -Any selection MAY begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. +Every selection MUST begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. The optional operator is idempotent, and repeated optionals (`.foo???`) MUST be treated as a single one. From 4094d5878b58f5d35055a3b93fccda0b8329ebae Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Tue, 6 Aug 2024 00:07:54 -0700 Subject: [PATCH 128/134] Fix typos & every/some -> all/any --- README.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f450a83..3ac41c7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The Delegation payload MUST describe the authorization claims, who is involved, # Capability [Capability]: #capability -A capability is the semantically-relevant claim of a delegation. They MUST be presented as a map under the `cap` field as a map. This map is REQUIRED but MAY be empty. This MUST take the following form: +A capability is the semantically-relevant claim of a delegation. They MUST take the following form: | Field | Type | Required | Description | |--------|---------------|----------|----------------------------------------------------------------------------------------------------------| @@ -83,8 +83,8 @@ Here is an illustrative example: "cmd": "/blog/post/create", "pol": [ ["==", ".status", "draft"], - ["every", ".reviewer", ["like", ".email", "*@example.com"]], - ["some", ".tags", + ["all", ".reviewer", ["like", ".email", "*@example.com"]], + ["any", ".tags", ["or", ["==", ".", "news"], ["==", ".", "press"]]] @@ -193,11 +193,11 @@ Delegation covers exact [Command] specified and all the commands described by a # Policy [Policy]: #policy -UCAN Delegation uses predicate logic statements extended with [jq]-style selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. +UCAN Delegation uses predicate logic statements extended with [jq]-inspired selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation]. A Policy is always given as an array of predicates. This top-level array is implicitly treated as a logical `and`, where `args` MUST pass validation of every top-level predicate. -Policies are structured as trees. With the exception of subtrees under `some`, `or`, and `not`, every leaf MUST evaluate to `true`. `some`, `or`, and `not` must +Policies are structured as trees. With the exception of subtrees under `any`, `or`, and `not`, every leaf MUST evaluate to `true`. A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for negation which MUST take the form `["not", statement]`. @@ -217,8 +217,8 @@ connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Neg / "[" DQUOTE "and" DQUOTE ",[" statement *("," statement) "]]" ; Conjuction / "[" DQUOTE "or" DQUOTE ",[" statement *("," statement) "]]" ; Disjunction -quantifier = "[" DQUOTE "every" DQUOTE "," selector "," policy "]" ; Universal - / "[" DQUOTE "some" DQUOTE "," selector "," policy "]" ; Existential +quantifier = "[" DQUOTE "all" DQUOTE "," selector "," policy "]" ; Universal + / "[" DQUOTE "any" DQUOTE "," selector "," policy "]" ; Existential ;; COMPARISONS @@ -365,19 +365,19 @@ When a selector resolves to a collection (an array or map), quantifiers provide Quantifying over an array is straightforward: it MUST apply the inner statement to each array value. Quantifying over a map MUST extract the values (discarding the keys), and then MUST proceed onthe values the same as if it were an array. -| Operator | Argument(s) | Example | -|----------|-------------------------|----------------------------------| -| `every` | `Selector, [Statement]` | `["every", ".a" [">", ".b", 1]]` | -| `some` | `Selector, [Statement]` | `["some", ".a" [">", ".b", 1]]` | +| Operator | Argument(s) | Example | +|----------|-------------------------|---------------------------------| +| `all` | `Selector, [Statement]` | `["all", ".a" [">", ".b", 1]]` | +| `any` | `Selector, [Statement]` | `["any", ".a" [">", ".b", 1]]` | -`every` extends `and` over collections. `some` extends `or` over collections. For example: +`all` extends `and` over collections. `any` extends `or` over collections. For example: ``` js const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]} -const statement = ["every", ".a", [">", ".b", 0]] +const statement = ["all", ".a", [">", ".b", 0]] // Outer Selector Substitution -["every", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], [">", ".b", 0]] +["all", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], [">", ".b", 0]] // Predicate Reduction ["and", [ @@ -397,10 +397,10 @@ false // ❌ ``` js const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]} -const statement = ["some", ".a", ["==", ".b", 2]] +const statement = ["any", ".a", ["==", ".b", 2]] // Reduction -["some", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], ["==", ".b", 2]] +["any", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], ["==", ".b", 2]] ["or", [ ["==", 1, 2], @@ -423,8 +423,8 @@ true // ✅ Quantified statements MAY be nested. For example, the below states that someone with the email `fraud@example.com` is required to be among the receipts of every newsletter. ``` js -["every", ".newsletters", - ["some", ".recipients", +["all", ".newsletters", + ["any", ".recipients", ["==", ".email", "fraud@example.com"]]] ``` @@ -575,6 +575,8 @@ Bytes MAY be selected into. When doing so, they MUST be treated as a byte array jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the optional (`?`) operator: UCAN's optional selector operator MUST return `null` for the failure case. +There are + ## Validation [Validation]: #validation @@ -600,7 +602,7 @@ Below is a step-by-step evaluation example: "cmd": "/msg", "pol": [ ["==", ".from", "alice@example.com"], - ["some", ".to", ["like", ".", "*@example.com"]] + ["any", ".to", ["like", ".", "*@example.com"]] ], // ... } @@ -609,12 +611,12 @@ Below is a step-by-step evaluation example: ``` js [ // Extract policy ["==", ".from", "alice@example.com"], - ["some", ".to", ["like", ".", "*@example.com"]] + ["any", ".to", ["like", ".", "*@example.com"]] ] [ // Resolve selectors ["==", "alice@example.com", "alice@example.com"], - ["some", ["bob@example.com", "carol@elsewhere.example.com"], ["like", ".", "*@example.com"]] + ["any", ["bob@example.com", "carol@elsewhere.example.com"], ["like", ".", "*@example.com"]] ] [ // Expand quantifier @@ -657,7 +659,7 @@ Note that this also applies to arrays and objects. For example, the `to` array i "cmd": "/email/send", "pol": [ ["==", ".from", "alice@example.com"], - ["some", ".to", ["like", ".", "*@example.com"]] + ["any", ".to", ["like", ".", "*@example.com"]] ] // ... } From 65c059fef5509f147b4c38c92badbeb6d973b26c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 1 Sep 2024 18:20:36 -0700 Subject: [PATCH 129/134] Switch to IPLD Schema --- README.md | 122 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 93 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 3ac41c7..8ee2bb2 100644 --- a/README.md +++ b/README.md @@ -204,46 +204,110 @@ A Policy is an array of statements. Every statement MUST take the form `[operato Below is a formal syntax for the UCAN Policy Language given in [ABNF] (representing IPLD as [DAG-JSON]): ``` abnf -policy = "[" *1(statement *("," statement)) "]" -statement = equality - / inequality - / wildcard - / connective - / quantifier +selector = DQUOTE "." DQUOTE ; Identity + / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with possible optionals -;; STRUCTURAL +subselector = "." CHAR string ; Dotted field selector + / *1(".") "[\" DQUOTE string "\" DQUOTE "]" ; Explicit field selector + / *1(".") "[" integer "]" ; Index selector + / *1(".") "[]" ; Collection values // FIXME doble check code +``` -connective = "[" DQUOTE "not" DQUOTE "," statement "]" ; Negation - / "[" DQUOTE "and" DQUOTE ",[" statement *("," statement) "]]" ; Conjuction - / "[" DQUOTE "or" DQUOTE ",[" statement *("," statement) "]]" ; Disjunction -quantifier = "[" DQUOTE "all" DQUOTE "," selector "," policy "]" ; Universal - / "[" DQUOTE "any" DQUOTE "," selector "," policy "]" ; Existential -;; COMPARISONS +``` ipldsch +-- Statements -equality = "[" DQUOTE "==" DQUOTE "," selector "," ipld "]" ; Equality on IPLD literals -inequality = "[" DQUOTE ">" DQUOTE "," selector "," number "]" ; Numeric greater-than - / "[" DQUOTE ">=" DQUOTE "," selector "," number "]" ; Numeric greater-than-or-equal - / "[" DQUOTE "<" DQUOTE "," selector "," number "]" ; Numeric lesser-than - / "[" DQUOTE "<=" DQUOTE "," selector "," number "]" ; Numeric lesser-than-or-equal +type Statement union { + | Equality + | Inequality + | Connective + | Negation + | Quantifier +} -wildcard = "[" DQUOTE "like" DQUOTE "," selector "," pattern "]" ; String wildcard matching +-- Equality -;; SELECTORS +type EqOp enum { + | Eq ("==") + | Neq ("!=") +} -selector = DQUOTE "." DQUOTE ; Identity - / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with possible optionals +type Equality struct { + op EqOp + sel Selector + val Any +} representation tuple -subselector = "." CHAR string ; Dotted field selector - / *1(".") "[\" DQUOTE string "\" DQUOTE "]" ; Explicit field selector - / *1(".") "[" integer "]" ; Index selector - / *1(".") "[]" ; Collection values // FIXME doble check code +type LikeOp enum { + | Like ("like") +} + +type Like struct { + op LikeOp + sel Selector + str Wildcard +} representation tuple + +-- Inequality + +type IneqOp enum { + | GT (">") + | GTE (">=") + | LT ("<") + | LTE ("<=") +} + +type Inequality struct { + op IneqOp + sel Selector + val Number +} representation tuple + +-- Connectives + +type NegateOp { + | Not ("not") +} + +type Negation struct { + op NegateOp + smts [Statement] +} representation tuple + +type ConnectiveOp enum { + | And ("and") + | Or ("or") +} + +type Connective struct { + op ConnectiveOp + smts [Statement] +} representation tuple + +-- Quantification + +type QuantifierOp enum { + | All ("all") + | Any ("any") +} + +type Quantifier struct { + op QuantiefierOp + sel Selector + smt Statement +} representation tuple + +-- Primitives + +type Selector = string -;; SPECIAL LITERALS +type Number union { + | NumInt int + | NumFloat float +} representation kinded -pattern = DQUOTE string DQUOTE ; Reminder: IPLD strings are UTF-8 -number = integer / float +type Wildcard = string ``` ## Comparisons From ac946a52512e7054a08ee879145fbac48cd83c01 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 1 Sep 2024 18:23:12 -0700 Subject: [PATCH 130/134] Clarify DID are strings --- README.md | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8ee2bb2..10d699d 100644 --- a/README.md +++ b/README.md @@ -49,17 +49,17 @@ The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0-rc.1`. The Delegation payload MUST describe the authorization claims, who is involved, and its validity period. -| Field | Type | Required | Description | -|---------|-------------------------------------------|----------|-------------------------------------------------------------| -| `iss` | `DID` | Yes | Issuer DID (sender) | -| `aud` | `DID` | Yes | Audience DID (receiver) | -| `sub` | `DID \| null` | Yes | Principal that the chain is about (the [Subject]) | -| `cmd` | `String` | Yes | The [Command] to eventually invoke | -| `pol` | `Policy` | Yes | [Policy] | -| `nonce` | `Bytes` | Yes | Nonce | -| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | -| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | -| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | +| Field | Type | Required | Description | +|---------|-------------------------------------------|----------|---------------------------------------------------------------------------| +| `iss` | `DID` | Yes | Issuer DID (sender). All [DID][did-spec]s are represented as string URLs. | +| `aud` | `DID` | Yes | Audience DID (receiver) | +| `sub` | `DID \| null` | Yes | Principal that the chain is about (the [Subject]) | +| `cmd` | `String` | Yes | The [Command] to eventually invoke | +| `pol` | `Policy` | Yes | [Policy] | +| `nonce` | `Bytes` | Yes | Nonce | +| `meta` | `{String : Any}` | No | [Meta] (asserted, signed data) — is not delegated authority | +| `nbf` | `Integer` (53-bits[^js-num-size]) | No | "Not before" UTC Unix Timestamp in seconds (valid from) | +| `exp` | `Integer \| null` (53-bits[^js-num-size]) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) | [^js-num-size]: JavaScript has a single numeric type ([`Number`][JS Number]) for both integers and floats. This representation is defined as a [IEEE-754] double-precision floating point number, which has a 53-bit significand. @@ -201,20 +201,6 @@ Policies are structured as trees. With the exception of subtrees under `any`, `o A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for negation which MUST take the form `["not", statement]`. -Below is a formal syntax for the UCAN Policy Language given in [ABNF] (representing IPLD as [DAG-JSON]): - -``` abnf -selector = DQUOTE "." DQUOTE ; Identity - / DQUOTE 1*(subselector *1("?")) DQUOTE ; Nested subselectors with possible optionals - -subselector = "." CHAR string ; Dotted field selector - / *1(".") "[\" DQUOTE string "\" DQUOTE "]" ; Explicit field selector - / *1(".") "[" integer "]" ; Index selector - / *1(".") "[]" ; Collection values // FIXME doble check code -``` - - - ``` ipldsch -- Statements @@ -954,6 +940,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [`did:web`]: https://w3c-ccg.github.io/did-method-web/ [base32]: https://github.com/multiformats/multibase/blob/master/multibase.csv#L13 [dag-json multicodec]: https://github.com/multiformats/multicodec/blob/master/table.csv#L112 +[did-spec]: https://www.w3.org/TR/did-core/ [did:key ECDSA]: https://w3c-ccg.github.io/did-method-key/#p-256 [did:key EdDSA]: https://w3c-ccg.github.io/did-method-key/#ed25519-x25519 [did:key RSA]: https://w3c-ccg.github.io/did-method-key/#rsa From 0dbaca11cdd0c6a5ccb89d77db3f5fb0d2245347 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sun, 1 Sep 2024 18:40:10 -0700 Subject: [PATCH 131/134] Correct FIXME --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 10d699d..ff10754 100644 --- a/README.md +++ b/README.md @@ -744,9 +744,8 @@ Note that this also applies to arrays and objects. For example, the `to` array i Other semantic conditions that are not possible to fully express syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that infromation in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field). -// FIXME duplicate header -# Validation -[Validation]: #validation +# Token Validation +[Token Validation]: #token-validation Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation] _prior to execution_. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation]s), each delegate MUST store one or more valid delegations chains for a particular claim. From a1dc160f291a7ecec94559f560f314f39d199771 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Oct 2024 11:18:37 -0500 Subject: [PATCH 132/134] Clarify list selectors --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ff10754..a4c71ef 100644 --- a/README.md +++ b/README.md @@ -485,16 +485,16 @@ Selector syntax is closely based on [jq]'s "filters". They operate on an [Invoca Selectors MUST only include the following features: -| Selector Name | Examples | Notes | -|---------------------------|-------------------------------------|-------------------------------------------------------------------------------------------------| -| Identity | `.` | Take the entire argument | -| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | -| Unambiguous field name | `["."]`, `["$_*"]` | Select in a map by arbitrary key | -| Collection values | `[]` | Expands out all of the children that match the remaining path FIXME double check with the code | -| Collection index | `[0]`, `[42]` | The element at an index. On a map, this is decided by IPLD's key sort order. FIXME double check | -| Collection negative index | `[-1]`, `[-42]` | The element by index from the end. `-1` is the index for the last element. | -| Collection slices | `[7:11]`, `[2:]`, `[:42]`, `[0:-2]` | The range of elements by their indices. | -| Optional | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | +| Selector Name | Examples | Notes | +|------------------------|-------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| Identity | `.` | Take the entire argument | +| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) | +| Unambiguous field name | `["."]`, `["$_*"], ["1"]` | Select in a map by arbitrary key | +| Collection values | `[]` | Expands out all of the children that match the remaining path. On lists this is a noop. On maps, this extracts values. | +| List index | `[0]`, `[42]` | The list element of a list by 0-index. | +| Negative list index | `[-1]`, `[-42]` | The list element by index from the end. `-1` is the index for the last element. | +| List slices | `[7:11]`, `[2:]`, `[:42]`, `[0:-2]` | The range of elements by their indices. | +| Optional | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail | Every selection MUST begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector. From f2108ca4ff5ac321505aba4b7122e0b3cc71785e Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Oct 2024 11:25:01 -0500 Subject: [PATCH 133/134] Thank the right people --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a4c71ef..9983a96 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - [Brooklyn Zelenka], [Witchcraft Software] - [Daniel Holmgren], [Bluesky] -- [Irakli Gozalishvili], [Protocol Labs] +- [Irakli Gozalishvili], [Common Tools] - [Philipp Krüger], [number zero] ## Dependencies @@ -22,7 +22,7 @@ ## Language [Language]: #language -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119]. +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14] when, and only when, they appear in all capitals, as shown here. # Abstract [Abstract]: #abstract @@ -864,7 +864,7 @@ Many thanks to [Christine Lemmer-Webber] for her handwritten(!) feedback on the Thanks to [Benjamin Goering] for the many community threads and connections to [W3C] standards. -Thanks to [Steve Moyer] for his detailed feedback on the selector design and thoughts on ANBF codegen. +Thanks to [Michael Muré] and [Steve Moyer] at [Infura] for their detailed feedback on the selector design and thoughts on ANBF codegen, and an updated Golang UCAN implementation. Thanks to [Juan Caballero] for the numerous questions, clarifications, and general advice on putting together a comprehensible spec. @@ -877,9 +877,10 @@ Many thanks to [Alan Karp] for sharing his vast experience with capability-based We want to especially recognize [Mark Miller] for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large. - + [ABNF]: https://datatracker.ietf.org/doc/html/rfc5234 [Alan Karp]: https://github.com/alanhkarp +[BCP 14]: https://www.rfc-editor.org/info/bcp14 [Benjamin Goering]: https://github.com/gobengo [Blaine Cook]: https://github.com/blaine [Bluesky]: https://blueskyweb.xyz/ @@ -889,6 +890,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [CIDv1]: https://github.com/multiformats/cid?tab=readme-ov-file#cidv1 [Christine Lemmer-Webber]: https://github.com/cwebber [Christopher Joel]: https://github.com/cdata +[Common Tools]: https://common.tools/ [DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/ [DAG-JSON]: https://ipld.io/specs/codecs/dag-json/spec/ [DID fragment]: https://www.w3.org/TR/did-core/#terminology @@ -903,6 +905,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Hugo Dias]: https://github.com/hugomrdias [IEEE-754]: https://ieeexplore.ieee.org/document/8766229 [IPLD]: https://ipld.io/ +[Infura]: https://www.infura.io/ [Ink & Switch]: https://www.inkandswitch.com/ [Invocation]: https://github.com/ucan-wg/invocation [Irakli Gozalishvili]: https://github.com/Gozala @@ -910,6 +913,7 @@ We want to especially recognize [Mark Miller] for his numerous contributions to [Juan Caballero]: https://github.com/bumblefudge [Mark Miller]: https://github.com/erights [Martin Kleppmann]: https://martin.kleppmann.com/ +[Michael Muré]: https://github.com/MichaelMure [Mikael Rogers]: https://github.com/mikeal/ [OCAP]: https://en.wikipedia.org/wiki/Object-capability_model [OCapN]: https://github.com/ocapn/ From b7b066bc09d881fd69f0651d0567af419023269c Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Mon, 21 Oct 2024 11:29:27 -0500 Subject: [PATCH 134/134] Clarify jq differences section --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9983a96..99d839d 100644 --- a/README.md +++ b/README.md @@ -621,11 +621,11 @@ Bytes MAY be selected into. When doing so, they MUST be treated as a byte array ### Differences from jq [Differences from jq]: #differences-from-jq -[jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which MUST NOT be supported in the UCAN Policy language. +[jq] is a much larger language than UCAN's selectors. jq includes features like pipes, arithmatic, regexes, assignment, recursive descent, and so on which are not supported in the UCAN Policy language, and thus MUST NOT be implemented in UCAN. -jq produces streams of values, in contrast to UCAN argument selectors which return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the optional (`?`) operator: UCAN's optional selector operator MUST return `null` for the failure case. +jq produces streams of values (a distrinct concept from arrays), in contrast to UCAN argument selectors which always return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the optional (`?`) operator: UCAN's optional selector operator MUST return `null` for the failure case. -There are +There are FIXME ## Validation [Validation]: #validation