diff --git a/index.html b/index.html index e002d701..9e6fc2c8 100644 --- a/index.html +++ b/index.html @@ -1730,6 +1730,422 @@

Query By Example

such as bbs-2023 or ecdsa-sd-2023 in the acceptedCryptosuites array.

+
+

Matching a Credential to a QueryByExample

+ +

+The following algorithm specifies how a conforming [=holder=] implementation +determines whether a [=verifiable credential=] matches the selection criteria +in a QueryByExample credentialQuery object. Required inputs are a +[=verifiable credential=] ([=map=] credential) and a +credentialQuery object ([=map=] credentialQuery). A +boolean match result is produced as output. A conforming implementation MUST +produce the same output as this algorithm. +

+ +

+Matching proceeds in two stages. First, if an example is present, +each of its properties is treated as a requirement against +credential. Second, if acceptedIssuers is present, the +credential's issuer MUST be acceptable. The credential matches only if both +stages succeed when present. +

+ +
    +
  1. +If credential is not a [=map=], return false. +
  2. +
  3. +If credentialQuery contains an example property, +initialize example to its value and perform the following steps: +
      +
    1. +If example contains an @context property, initialize +expectedContexts to its value normalized to a [=list=], and +initialize credentialContexts to the value of the +@context property of credential normalized to a +[=list=]. If the length of credentialContexts is less than the +length of expectedContexts, return false. For each +index i from 0 to the length of +expectedContexts minus one, if +expectedContexts[i] is not strictly equal to +credentialContexts[i], return false. +
    2. +
    3. +If example contains a type property, initialize +expectedTypes to its value normalized to a [=list=], and initialize +credentialTypes to the value of the type property of +credential normalized to a [=list=]. If no value in +expectedTypes is strictly equal to any value in +credentialTypes, return false. +
    4. +
    5. +Initialize remaining to a copy of example with the +@context and type properties removed, if present. If +remaining contains any properties, set match to the +result of running the algorithm in Section +[[[#matching-object-properties]]], passing remaining as +expectedObject and credential as +actualObject. If match is false, return +false. +
    6. +
    +
  4. +
  5. +If credentialQuery contains an acceptedIssuers +property, initialize acceptedIssuers to its value and perform the +following steps: +
      +
    1. +Initialize credentialIssuer to the value of the +issuer property of credential. If +credentialIssuer is a [=map=], initialize +credentialIssuer to the value of its id property. +
    2. +
    3. +Initialize issuerItems to an empty [=list=]. For each +item in acceptedIssuers, if item is a +[=list=], append each of its elements to issuerItems; otherwise, +append item to issuerItems. +
    4. +
    5. +If no item in issuerItems is acceptable for +credentialIssuer, return false. An item is +acceptable if any of the following is true: +
        +
      1. +item is a [=string=] that is strictly equal to +credentialIssuer. +
      2. +
      3. +item is a [=map=] that contains an id property whose +value is strictly equal to credentialIssuer. +
      4. +
      5. +item is a [=map=] that contains a recognizedIn +property, and credentialIssuer is recognized according to the +[[[VC-RECOG-ENT]]] specification using that property's value. +
      6. +
      +
    6. +
    +
  6. +
  7. +Return true. +
  8. +
+ +

+The reason, acceptedCryptosuites, and +acceptedEnvelopes properties of a credentialQuery +object are not used during credential matching. The reason +property is intended for display to the [=holder=] by their software. The +acceptedCryptosuites and acceptedEnvelopes properties +inform the [=holder=]'s software about which proof mechanisms and envelope +formats the [=verifier=] will accept, and are used during presentation +construction rather than credential selection. +

+ +

+This algorithm compares JSON structure and values. It does not require JSON-LD +compaction, expansion, or term rewriting. Implementations MAY apply JSON-LD +transformations before matching, provided the boolean result is the same as +this algorithm when run on the transformed inputs. +

+
+ +
+
Matching Object Properties
+ +

+The following algorithm determines whether every property of an expected object +is satisfied by an actual object. It is used recursively for nested objects +such as credentialSubject. Required inputs are an expected object +([=map=] expectedObject) and an actual object ([=map=] +actualObject). A boolean is produced as output. +

+ +
    +
  1. +For each propertyName and expectedValue pair in +expectedObject: +
      +
    1. +If actualObject does not contain a property named +propertyName, return false. +
    2. +
    3. +Initialize actualValue to the value of the +propertyName property of actualObject. +
    4. +
    5. +If expectedValue is an empty [=string=] (""), continue +to the next property. An empty string indicates that the property MUST exist, +but any value is acceptable. +
    6. +
    7. +If expectedValue is a [=map=]: +
        +
      1. +If expectedValue contains no properties, continue to the next +property. An empty map indicates that the property MUST exist, but any value is +acceptable. +
      2. +
      3. +If actualValue is a [=list=], then at least one element in +actualValue MUST be a [=map=] for which this algorithm returns +true when called with expectedValue as +expectedObject and the element as actualObject. If no +such element exists, return false. +
      4. +
      5. +Otherwise, set nestedMatch to the result of running this algorithm +with expectedValue as expectedObject and +actualValue as actualObject. If nestedMatch is +false, return false. +
      6. +
      +
    8. +
    9. +If expectedValue is a [=list=]: +
        +
      1. +If expectedValue contains no elements, continue to the next +property. An empty list indicates that the property MUST exist, but any value +is acceptable. +
      2. +
      3. +Initialize actualValues to actualValue normalized to a +[=list=] (if actualValue is not already a [=list=], wrap it in a +[=list=] containing only actualValue). +
      4. +
      5. +At least one element expectedElement in expectedValue +MUST match at least one element actualElement in +actualValues. An expectedElement matches an +actualElement if any of the following is true: +
          +
        1. +expectedElement is a [=map=], actualElement is a +[=map=], and this algorithm returns true when called with +expectedElement as expectedObject and +actualElement as actualObject. +
        2. +
        3. +expectedElement is not a [=map=] and +actualElement is strictly equal to expectedElement. +
        4. +
        5. +expectedElement is not a [=map=] and numeric coercion applies: +both actualElement and expectedElement can be +interpreted as numeric values (using the same eligibility rules as for +primitive values below), and their numeric values are equal. +
        6. +
        +If no such matching pair exists, return false. +
      6. +
      +
    10. +
    11. +Otherwise, expectedValue is a primitive value. Initialize +actualValues to actualValue normalized to a [=list=]. At +least one element in actualValues MUST satisfy one of the following: +
        +
      1. +The element is strictly equal to expectedValue. +
      2. +
      3. +Numeric coercion: both the element and expectedValue can be +interpreted as numeric values, and their numeric values are equal. +Implementations SHOULD perform numeric coercion when equivalent numeric values +are represented differently (for example, the [=string=] "21" and +the number 21). A value is eligible for coercion only if it is a +[=string=] that represents a valid numeric value in its entirety; strings that +contain non-numeric characters MUST NOT be coerced. +
      4. +
      +
    12. +
    +
  2. +
  3. +Return true. +
  4. +
+ +

+This recursive property matching algorithm is functionally equivalent to +flattening the expected object into a map of +JSON Pointer paths paired +with expected values, and then checking each path against the credential. +Implementations MAY use either approach, provided the results are equivalent. +See also Section [[[#converting-querybyexample-to-json-pointers]]]. +

+
+ +
+

Converting QueryByExample to JSON Pointers

+ +

+The following algorithm converts a QueryByExample example object +into a list of JSON +Pointers that specify which fields to reveal for [=selective disclosure=], +such as the selectivePointers option used with credential +derivation (see Section [[[#derive-credential]]]). Required input is an +example object ([=map=] example). An array of JSON Pointer strings +(pointers) is produced as output. A conforming implementation that +converts a QueryByExample example for [=selective disclosure=] +MUST produce the same output as this algorithm. +

+ +
    +
  1. +Initialize object to a copy of example with the +@context property removed, if present. +
  2. +
  3. +Initialize pointers to the list of JSON Pointers that identify every +value in object (for example, by treating object as a +tree and emitting a pointer for each leaf value, including properties whose +value is an empty [=string=]). +
  4. +
  5. +Append /type and /issuer to pointers if +they are not already present. These pointers are the deterministic mandatory +reveal paths for selective disclosure conversion under +[[VC-DATA-MODEL-2.0]]. Securing mechanisms MAY require additional mandatory +pointers at issuance time; those are outside the scope of this conversion. +
  6. +
  7. +Set pointers to the result of running the algorithm in Section +[[[#adjust-reveal-pointers]]], passing pointers. +
  8. +
  9. +Return pointers. +
  10. +
+ +

+The following example uses the Permanent Resident Card QueryByExample from this +section. After removing @context, generating leaf pointers +(including /credentialSubject/birthCountry from the empty-string +claim), appending the mandatory /type and /issuer +pointers, ensuring credentialSubject coverage, pruning shallow +pointers, and normalizing type pointers, a conforming conversion +produces: +

+ +
+{
+  "selectivePointers": [
+    "/type",
+    "/issuer",
+    "/credentialSubject/birthCountry"
+  ]
+}
+          
+ +

+In this result, /type and /issuer are always added by +the conversion algorithm as mandatory reveal pointers, and +/credentialSubject/birthCountry is the claim requested by the +example (via an empty-string value). These pointers are suitable for use as +selectivePointers when deriving a credential for presentation. +

+
+ +
+
Adjust Reveal Pointers
+ +

+The following algorithm adjusts a list of JSON Pointers produced from a +QueryByExample example so they are suitable for selective +disclosure. Required input is an array of JSON Pointer strings +(pointers). An array of JSON Pointer strings is produced as output. +

+ +
    +
  1. +If no pointer in pointers is equal to +/credentialSubject and no pointer has +/credentialSubject/ as a prefix, append +/credentialSubject to pointers. +
  2. +
  3. +Set pointers to the result of running the algorithm in Section +[[[#prune-shallow-pointers]]], passing pointers. +
  4. +
  5. +Initialize adjustedPointers to an empty [=list=]. For each +pointer in pointers: +
      +
    1. +Initialize typeIndex to the index of the substring +/type/ in pointer, or -1 if it does not +occur. +
    2. +
    3. +If typeIndex is not -1, append the substring of +pointer from the start through /type (that is, truncate +any array index after /type) to adjustedPointers. +Otherwise, append pointer to adjustedPointers. +
    4. +
    +
  6. +
  7. +Return adjustedPointers. +
  8. +
+
+ +
+
Prune Shallow Pointers
+ +

+The following algorithm retains only the deepest JSON Pointers from a list, so +that a shallow pointer is removed when a deeper pointer under the same path +exists. Required input is an array of JSON Pointer strings +(pointers). An array of JSON Pointer strings is produced as output. +

+ +

+For example, given +["/a/b", "/a/b/c", "/a/b/c/d"], the result is +["/a/b/c/d"]. +

+ +
    +
  1. +Initialize deep to an empty [=list=]. +
  2. +
  3. +For each pointer in pointers: +
      +
    1. +Initialize isDeep to true. +
    2. +
    3. +For each other in pointers: +
        +
      1. +If the length of pointer is less than the length of +other and other starts with pointer, set +isDeep to false and stop comparing for this +pointer. +
      2. +
      +
    4. +
    5. +If isDeep is true, append pointer to +deep. +
    6. +
    +
  4. +
  5. +Return deep. +
  6. +
+
+
@@ -2352,6 +2768,11 @@

Workflows and Exchanges

more complex workflow templates, can be found in the appendix Workflow Step and Template Examples.

+An informative reference algorithm for walking [=exchange=] steps until client +input is required or a response is ready is provided in Appendix +[[[#reference-workflow-step-processing-algorithm]]]. +

+

A given interaction with an exchange is expected to be short-lived but other mechanisms can be used to enable longer or multi-stage interactions. Examples of other interaction mechanisms include SMS, email, web notifications, @@ -4479,6 +4900,267 @@

Complex Examples

+
+

Reference Workflow Step Processing Algorithm

+ +

+This appendix is non-normative. +

+ +

+This specification does not mandate a single internal implementation for +advancing an [=exchange=]. The algorithm below documents one possible +approach for walking [=workflow=] steps until the [=workflow service=] must +return a partial response to the exchange client (for example, to +ask for a [=verifiable presentation=]), until the exchange completes, or until +the exchange fails or times out. Implementations MAY differ in ordering, +persistence, error handling, and protocol bindings. +

+ +

+Hooks such as prepareStep, +validateReceivedPresentation, +validateReceivedPresentationRequest, inputRequired, +createVerifiablePresentationRequest, and +isStepComplete exist so that the same logical loop can be adapted +when exchanges are fronted by this HTTP API, OpenID for Verifiable Presentations +(OID4VP), OpenID for Verifiable Credential Issuance (OID4VCI), invite-based +flows, or other interaction mechanisms. The response object +produced when the loop pauses or finishes can be returned directly to clients +using this API or mapped into other protocol messages as needed. +

+ +
+

Process Exchange Steps

+ +

+The following algorithm processes an exchange by repeatedly evaluating the +current workflow step. Required inputs are a workflow configuration ([=map=] +workflow) and an exchange ([=map=] exchange). Optional +inputs are a received [=verifiable presentation=] ([=map=] +receivedPresentation) and a received [=verifiable presentation +request=] ([=map=] receivedPresentationRequest). A response object +([=map=] response), which MAY be empty, is produced as output, or an +error is raised. +

+ +
    +
  1. +Initialize step and response to null. +
  2. +
  3. +If exchange.state is complete or +invalid, throw a NotAllowedError. +
  4. +
  5. +If exchange.state is pending, set it to +active. +
  6. +
  7. +Continuously loop to process exchange steps, optionally saving any error thrown +as exchange.lastError. Return out of the loop when a +full response is generated, input from the exchange client is required, or the +exchange times out. An implementation-specific maximum step count MAY be +enforced to handle misconfigured workflows. In each iteration: +
      +
    1. +Set step to the current step (evaluating a step template as needed). +
    2. +
    3. +Call the prepareStep hook, passing workflow, +exchange, step, receivedPresentation, and +receivedPresentationRequest, to perform any protocol-specific step +preparation. If prepareStep returns a result with +receivedPresentation and/or +receivedPresentationRequest set, update +receivedPresentation and/or receivedPresentationRequest +accordingly. +
    4. +
    5. +If receivedPresentation is set, call +validateReceivedPresentation, passing workflow, +exchange, step, and receivedPresentation. +
    6. +
    7. +If receivedPresentationRequest is set, call +validateReceivedPresentationRequest, passing exchange, +step, and receivedPresentationRequest. +
    8. +
    9. +If the implementation supports blocking callbacks that can return results to be +added to exchange variables (or return errors), call the callback and store its +results in +exchange.variables.results[exchange.step].callbackResults, or throw +any error received. +
    10. +
    11. +Set isInputRequired to the result of calling the +inputRequired hook, passing step and +receivedPresentation, to perform any protocol-specific input checks. +
    12. +
    13. +If isInputRequired is true: +
        +
      1. +If response is null, set it to an empty object. +
      2. +
      3. +If step.verifiablePresentationRequest is set, call +createVerifiablePresentationRequest, passing workflow, +exchange, step, and response. +
      4. +
      5. +Save the exchange (and call any non-blocking callback in the step) and return +response. +
      6. +
      +
    14. +
    15. +Set issueToClient to true if +step.issueRequests includes any issuer requests for +[=verifiable credentials=] that are to be sent to the client +(issueRequest.result is not set); otherwise set it to +false. +
    16. +
    17. +If step.verifiablePresentation is set or +issueToClient is true: +
        +
      1. +If response is not null: +
          +
        1. +If response.verifiablePresentationRequest is not set, +set it to an empty object (to indicate that the exchange is not yet complete). +
        2. +
        3. +Save the exchange (and call any non-blocking callback in the step). +
        4. +
        5. +Return response. +
        6. +
        +
      2. +
      3. +Set response to an empty object. +
      4. +
      5. +If step.verifiablePresentation is set, set +response.verifiablePresentation to a copy of it; +otherwise set response.verifiablePresentation to a new, +empty [=verifiable presentation=] (using [[VC-DATA-MODEL-2.0]] by default; a +custom configuration MAY specify another version). +
      6. +
      +
    18. +
    19. +Perform every issue request (optionally in parallel), returning an error +response to the client if any fails. Implementations MAY implement failure +recovery or retry issue requests at their own discretion: +
        +
      1. +For each issue request where result is set to an exchange variable +path or name, save the issued credential in the referenced exchange variable. +
      2. +
      3. +For each issue request where result is not specified, save the +issued credential in +response.verifiablePresentation (for a VCDM +presentation, append it to +response.verifiablePresentation.verifiableCredential). +
      4. +
      +
    20. +
    21. +If response.verifiablePresentation is set and the step +configuration indicates it should be signed, sign the presentation (for example, +by using a VCALM holder instance's /presentations create endpoint). +
    22. +
    23. +Call isStepComplete, passing workflow, +exchange, step, receivedPresentation, and +receivedPresentationRequest, to perform any protocol-specific +behavior to determine if the step is complete. Set stepComplete to +the result, defaulting to true. +
    24. +
    25. +If stepComplete is true: +
        +
      1. +If step.redirectUrl is set: +
          +
        1. +If response is null, set it to an empty object. +
        2. +
        3. +Set response.redirectUrl to +step.redirectUrl. +
        4. +
        +
      2. +
      3. +If step.nextStep is not set, set +exchange.state to complete. +
      4. +
      5. +Otherwise, delete +exchange.variables.results[step.nextStep] if it exists, and set +exchange.step to +step.nextStep. +
      6. +
      +
    26. +
    27. +Save the exchange (and call any non-blocking callback in the step). +
    28. +
    29. +If exchange.state is complete, return +response if it is not null; otherwise return an empty +object. +
    30. +
    31. +Set receivedPresentation to null and continue the loop. +
    32. +
    +
  8. +
+
+ +
+

Protocol and implementation hooks

+ +

+The following hooks are intentionally underspecified so that implementations can +bind the reference loop to a particular protocol or deployment: +

+ + +
+
+

Relationship to Other Specifications