Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

docs: Presentation Exchange - adds samples #2437

Merged
merged 1 commit into from
Jan 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions pkg/doc/presexch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Presentation Exchange

This document shows how to use [Presentation Exchange](https://identity.foundation/presentation-exchange) by examples.
Code examples can be found [here](example_test.go).

1. This example demonstrates `predicate` and `limit_disclosure` usage.
The `presentation definition` below requires field `age` to be greater or equal to 18.
Also, we have `limit_disclosure=true` which requires that output data is limited to the entries specified in the `fields` property.
The `predicate` means that the result should be expressed as a boolean value.
Copy link
Contributor

Choose a reason for hiding this comment

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

@soluchok

The predicate means that the result should be expressed as a boolean value.

This is a misunderstanding of the spec. The predicate is just criteria that the credential submitted in the presentation must satisfy. It is not a transformation function.

```json
{
"id": "31e2f0f1-6b70-411d-b239-56aed5321884",
"purpose": "To sell you a drink we need to know that you are an adult.",
"input_descriptors": [
{
"id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
"purpose": "Your age should be greater or equal to 18.",
"schema": [
{
"uri": "https://www.w3.org/TR/vc-data-model/#types"
}
],
"constraints": {
"limit_disclosure": true,
"fields": [
{
"path": [
"$.age"
],
"filter": {
"type": "integer",
"minimum": 18
},
"predicate": "required"
}
]
}
}
]
}
```
Let's say we have such a credential in our database.
```json
{
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"age": 21,
"credentialSchema": [
{
"id": "https://www.w3.org/TR/vc-data-model/#types"
}
],
"first_name": "Jesse",
"id": "2dc74354-e965-4883-be5e-bfec48bf60c7",
"issuer": "",
"last_name": "Pinkman",
"type": "VerifiableCredential"
}
```
As a result, we will have the following `verifiable presentation`:
```json
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://identity.foundation/presentation-exchange/submission/v1"
],
"presentation_submission": {
"id": "accd5adf-1dbf-4ed9-9ba2-d687476126cb",
"definition_id": "31e2f0f1-6b70-411d-b239-56aed5321884",
"descriptor_map": [
{
"id": "867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
"format": "ldp_vp",
"path": "$.verifiableCredential[0]"
}
]
},
"type": [
"VerifiablePresentation",
"PresentationSubmission"
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"age": true,
Copy link
Contributor

Choose a reason for hiding this comment

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

@soluchok

This is a different claim than the one originally issued (by the Issuer). This new claim is not covered by cryptographic proof from the Issuer; it is unverifiable.

How will this work in practice?

"credentialSchema": [
{
"id": "https://www.w3.org/TR/vc-data-model/#types"
}
],
"credentialSubject": null,
"id": "2dc74354-e965-4883-be5e-bfec48bf60c7",
"issuer": "",
"type": "VerifiableCredential"
}
]
}
```

As you can see the VP has a credential without `first_name` and `last_name` (because of `limit_disclosure`).
Also, instead of `age`, we have a boolean value (because of `predicate`).
soluchok marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 2 additions & 1 deletion pkg/doc/presexch/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package presexch
package presexch_test

import (
"encoding/json"
Expand All @@ -17,6 +17,7 @@ import (
"github.com/piprate/json-gold/ld"
"github.com/stretchr/testify/require"

. "github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
"github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
)
Expand Down
40 changes: 37 additions & 3 deletions pkg/doc/presexch/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"encoding/json"
"errors"
"fmt"
"sort"
"strings"

"github.com/PaesslerAG/jsonpath"
Expand All @@ -33,6 +34,8 @@ const (
Required Preference = "required"
// Preferred predicate`s value.
Preferred Preference = "preferred"

tmpEnding = "tmp_unique_id_"
)

// nolint: gochecknoglobals
Expand Down Expand Up @@ -318,7 +321,7 @@ func (pd *PresentationDefinition) CreateVP(credentials ...*verifiable.Credential

credentials, descriptors := merge(result)
vp.CustomFields = verifiable.CustomFields{
submissionProperty: PresentationSubmission{
submissionProperty: &PresentationSubmission{
ID: uuid.New().String(),
DefinitionID: pd.ID,
DescriptorMap: descriptors,
Expand Down Expand Up @@ -530,7 +533,7 @@ func filterConstraints(constraints *Constraints, creds []*verifiable.Credential)

if constraints.LimitDisclosure {
template, err = json.Marshal(map[string]interface{}{
"id": credential.ID,
"id": tmpID(credential.ID),
"credentialSchema": credential.Schemas,
"type": credential.Types,
"@context": credential.Context,
Expand All @@ -557,6 +560,19 @@ func filterConstraints(constraints *Constraints, creds []*verifiable.Credential)
return result, nil
}

func tmpID(id string) string {
return id + tmpEnding + uuid.New().String()
}

func trimTmpID(id string) string {
idx := strings.Index(id, tmpEnding)
if idx == -1 {
return id
}

return id[:idx]
}

func createNewCredential(fs []*Field, src, limitedCred []byte) (*verifiable.Credential, error) {
for _, f := range fs {
paths, err := jsonpathkeys.ParsePaths(f.Path...)
Expand Down Expand Up @@ -676,9 +692,19 @@ func merge(setOfCredentials map[string][]*verifiable.Credential) ([]*verifiable.
descriptors []*InputDescriptorMapping
)

for descriptorID, credentials := range setOfCredentials {
keys := make([]string, 0, len(setOfCredentials))
for k := range setOfCredentials {
keys = append(keys, k)
}

sort.Strings(keys)

for _, descriptorID := range keys {
credentials := setOfCredentials[descriptorID]

for _, credential := range credentials {
if _, ok := setOfCreds[credential.ID]; !ok {
credential.ID = trimTmpID(credential.ID)
result = append(result, credential)
setOfCreds[credential.ID] = len(descriptors)
}
Expand All @@ -694,9 +720,17 @@ func merge(setOfCredentials map[string][]*verifiable.Credential) ([]*verifiable.
}
}

sort.Sort(byID(descriptors))

return result, descriptors
}

type byID []*InputDescriptorMapping

func (a byID) Len() int { return len(a) }
func (a byID) Less(i, j int) bool { return a[i].ID < a[j].ID }
func (a byID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

func credentialsToInterface(credentials []*verifiable.Credential) []interface{} {
var result []interface{}
for i := range credentials {
Expand Down
Loading