forked from hyperledger-archives/aries-framework-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 0510: Presentation-Exchange Attachment (hyperledger-archives#2472)
Signed-off-by: Andrii Soluk <[email protected]>
- Loading branch information
1 parent
b71306f
commit c8c3795
Showing
8 changed files
with
430 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,23 +7,31 @@ SPDX-License-Identifier: Apache-2.0 | |
package presentproof | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/google/uuid" | ||
|
||
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" | ||
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof" | ||
"github.com/hyperledger/aries-framework-go/pkg/doc/presexch" | ||
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" | ||
vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" | ||
storeverifiable "github.com/hyperledger/aries-framework-go/pkg/store/verifiable" | ||
) | ||
|
||
const ( | ||
stateNamePresentationReceived = "presentation-received" | ||
stateNameRequestReceived = "request-received" | ||
myDIDKey = "myDID" | ||
theirDIDKey = "theirDID" | ||
namesKey = "names" | ||
|
||
mimeTypeApplicationLdJSON = "application/ld+json" | ||
verifiableCredentialType = "VerifiableCredential" | ||
peDefinitionFormat = "dif/presentation-exchange/[email protected]" | ||
peSubmissionFormat = "dif/presentation-exchange/[email protected]" | ||
) | ||
|
||
// Metadata is an alias to the original Metadata. | ||
|
@@ -90,6 +98,154 @@ func SavePresentation(p Provider) presentproof.Middleware { | |
} | ||
} | ||
|
||
type presentationExchangePayload struct { | ||
Challenge string `json:"challenge"` | ||
Domain string `json:"domain"` | ||
PresentationDefinition *presexch.PresentationDefinition `json:"presentation_definition"` | ||
} | ||
|
||
// PresentationDefinition the helper function for the present proof protocol that creates VP based on credentials that | ||
// were provided in the attachments according to the requested presentation definition. | ||
func PresentationDefinition(p Provider) presentproof.Middleware { | ||
vdr := p.VDRegistry() | ||
|
||
return func(next presentproof.Handler) presentproof.Handler { | ||
return presentproof.HandlerFunc(func(metadata presentproof.Metadata) error { | ||
if metadata.StateName() != stateNameRequestReceived { | ||
return next.Handle(metadata) | ||
} | ||
|
||
request := presentproof.RequestPresentation{} | ||
if err := metadata.Message().Decode(&request); err != nil { | ||
return fmt.Errorf("decode: %w", err) | ||
} | ||
|
||
if metadata.Presentation() == nil || | ||
!hasFormat(request.Formats, peDefinitionFormat) || | ||
hasFormat(metadata.Presentation().Formats, peSubmissionFormat) { | ||
return next.Handle(metadata) | ||
} | ||
|
||
src, err := getAttachmentByFormat(request.Formats, | ||
request.RequestPresentationsAttach, peDefinitionFormat) | ||
if err != nil { | ||
return fmt.Errorf("get attachment by format: %w", err) | ||
} | ||
|
||
var payload *presentationExchangePayload | ||
|
||
if err = json.Unmarshal(src, &payload); err != nil { | ||
return fmt.Errorf("unmarshal definition: %w", err) | ||
} | ||
|
||
credentials, err := parseCredentials(metadata.Presentation().PresentationsAttach) | ||
if err != nil { | ||
return fmt.Errorf("parse credentials: %w", err) | ||
} | ||
|
||
presentation, err := payload.PresentationDefinition.CreateVP(credentials, verifiable.WithPublicKeyFetcher( | ||
verifiable.NewDIDKeyResolver(vdr).PublicKeyFetcher(), | ||
)) | ||
if err != nil { | ||
return fmt.Errorf("create VP: %w", err) | ||
} | ||
|
||
metadata.Presentation().PresentationsAttach = append( | ||
metadata.Presentation().PresentationsAttach, decorator.Attachment{ | ||
ID: uuid.New().String(), | ||
MimeType: mimeTypeApplicationLdJSON, | ||
Data: decorator.AttachmentData{JSON: presentation}, | ||
}) | ||
|
||
return next.Handle(metadata) | ||
}) | ||
} | ||
} | ||
|
||
func contains(s []string, e string) bool { | ||
for _, a := range s { | ||
if a == e { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func parseCredentials(attachments []decorator.Attachment) ([]*verifiable.Credential, error) { // nolint: gocyclo | ||
var credentials []*verifiable.Credential | ||
|
||
for i := range attachments { | ||
if attachments[i].MimeType != mimeTypeApplicationLdJSON { | ||
continue | ||
} | ||
|
||
src, err := attachments[i].Data.Fetch() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var types struct { | ||
Type interface{} `json:"type"` | ||
} | ||
|
||
err = json.Unmarshal(src, &types) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var credentialTypes []string | ||
|
||
switch v := types.Type.(type) { | ||
case string: | ||
credentialTypes = []string{v} | ||
case []interface{}: | ||
for _, e := range v { | ||
if val, ok := e.(string); ok { | ||
credentialTypes = append(credentialTypes, val) | ||
} | ||
} | ||
} | ||
|
||
if !contains(credentialTypes, verifiableCredentialType) { | ||
continue | ||
} | ||
|
||
credential, err := verifiable.ParseUnverifiedCredential(src) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
credentials = append(credentials, credential) | ||
} | ||
|
||
return credentials, nil | ||
} | ||
|
||
func getAttachmentByFormat(fms []presentproof.Format, attachments []decorator.Attachment, name string) ([]byte, error) { | ||
for _, format := range fms { | ||
if format.Format == name { | ||
for i := range attachments { | ||
if attachments[i].ID == format.AttachID { | ||
return attachments[i].Data.Fetch() | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil, errors.New("not found") | ||
} | ||
|
||
func hasFormat(formats []presentproof.Format, format string) bool { | ||
for _, fm := range formats { | ||
if fm.Format == format { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func getName(idx int, id string, metadata presentproof.Metadata) string { | ||
name := id | ||
if len(metadata.PresentationNames()) > idx { | ||
|
Oops, something went wrong.