Skip to content

Commit

Permalink
use SubmissionBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed Nov 16, 2023
1 parent b4eb387 commit 4a90357
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 15 deletions.
35 changes: 35 additions & 0 deletions vcr/credential/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
package credential

import (
"errors"
"fmt"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/vcr/signature/proof"
)

// FindValidator finds the Validator the provided credential based on its Type
Expand Down Expand Up @@ -52,3 +56,34 @@ func ExtractTypes(credential vc.VerifiableCredential) []string {

return vcTypes
}

func PresentationSigner(presentation vc.VerifiablePresentation) (*did.DID, error) {
switch presentation.Format() {
case vc.JWTPresentationProofFormat:
token := presentation.JWT()
issuer := token.Issuer()
if issuer == "" {
return nil, errors.New("JWT presentation does not have 'iss' claim")
}
return did.ParseDID(issuer)
case vc.JSONLDCredentialProofFormat:
fallthrough
default:
var proofs []proof.LDProof
if err := presentation.UnmarshalProofValue(&proofs); err != nil {
return nil, fmt.Errorf("invalid LD-proof for presentation: %w", err)
}
if len(proofs) != 1 {
return nil, fmt.Errorf("presentation should have exactly 1 proof, got %d", len(proofs))
}
verificationMethod, err := did.ParseDIDURL(proofs[0].VerificationMethod.String())
if err != nil {
return nil, fmt.Errorf("invalid verification method for JSON-LD presentation: %w", err)
} else {
if verificationMethod.DID.Empty() {
return nil, fmt.Errorf("verification method for JSON-LD presentation does not have a DID")
}
return &verificationMethod.DID, nil
}
}
}
28 changes: 18 additions & 10 deletions vcr/pe/presentation_submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ package pe

import (
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/vcr/credential"
"github.com/nuts-foundation/nuts-node/vcr/log"
v2 "github.com/nuts-foundation/nuts-node/vcr/pe/schema/v2"
"reflect"
Expand Down Expand Up @@ -144,18 +144,23 @@ func (b *PresentationSubmissionBuilder) Build(format string) (PresentationSubmis
// Validate validates the Presentation Submission to the Verifiable Presentation and Presentation Definition and returns the mapped credentials.
// The credentials will be returned as map with the InputDescriptor.Id as key.
// It assumes credentials of the presentation only map in 1 way to the input descriptors.
func (s PresentationSubmission) Validate(presentation vc.VerifiablePresentation, definition PresentationDefinition) (map[string]vc.VerifiableCredential, error) {
expectedCredentials, expectedDescriptorMap, err := definition.Match(presentation.VerifiableCredential)
if err != nil {
return nil, fmt.Errorf("credential submission is invalid: %w", err)
func (s PresentationSubmission) Validate(presentations []vc.VerifiablePresentation, definition PresentationDefinition) (map[string]vc.VerifiableCredential, error) {
builder := definition.PresentationSubmissionBuilder()
for _, presentation := range presentations {
signer, err := credential.PresentationSigner(presentation)
if err != nil {
return nil, fmt.Errorf("unable to derive presentation signer: %w", err)
}
builder.AddWallet(*signer, presentation.VerifiableCredential)
}
if len(expectedCredentials) == 0 {
return nil, errors.New("credential submission is invalid, credentials do not match the presentation definition")
presentationSubmission, signInstructions, err := builder.Build("")
if err != nil {
return nil, err
}
// Marshal, then unmarshal descriptor mappings into interface{}, to make sure ordering and zero-handling is the same for both.
// Then, they can simply be compared to check that the submission
var expected interface{}
if err := remarshal(expectedDescriptorMap, &expected); err != nil {
if err := remarshal(presentationSubmission.DescriptorMap, &expected); err != nil {
return nil, err
}
var actual interface{}
Expand All @@ -169,9 +174,12 @@ func (s PresentationSubmission) Validate(presentation vc.VerifiablePresentation,
return nil, fmt.Errorf("credential submission is invalid, input descriptor mapping looks invalid")
}
credentialMap := make(map[string]vc.VerifiableCredential, len(s.DescriptorMap))
for i, inputDescriptor := range s.DescriptorMap {
credentialMap[inputDescriptor.Id] = expectedCredentials[i]
for _, signInstruction := range signInstructions {
for i, inputDescriptor := range signInstruction.inputDescriptorMappingObjects {
credentialMap[inputDescriptor.Id] = signInstruction.VerifiableCredentials[i]
}
}

return credentialMap, nil
}

Expand Down
77 changes: 72 additions & 5 deletions vcr/pe/presentation_submission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/vcr/pe/test"
"github.com/nuts-foundation/nuts-node/vcr/signature/proof"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
Expand Down Expand Up @@ -88,14 +89,17 @@ func TestPresentationSubmissionBuilder_Build(t *testing.T) {
}

func TestPresentationSubmission_Validate(t *testing.T) {
vcID := ssi.MustParseURI("first-vc")
vcID := ssi.MustParseURI("did:example:123#first-vc")
vp := vc.VerifiablePresentation{
VerifiableCredential: []vc.VerifiableCredential{
{ID: &vcID},
},
Proof: []interface{}{
proof.LDProof{VerificationMethod: vcID},
},
}

t.Run("ok", func(t *testing.T) {
t.Run("ok - 1 presentation", func(t *testing.T) {
constant := vcID.String()
definition := PresentationDefinition{
InputDescriptors: []*InputDescriptor{
Expand Down Expand Up @@ -124,7 +128,70 @@ func TestPresentationSubmission_Validate(t *testing.T) {
},
}

credentials, err := submission.Validate(vp, definition)
credentials, err := submission.Validate([]vc.VerifiablePresentation{vp}, definition)

assert.NoError(t, err)
assert.Len(t, credentials, 1)
assert.Equal(t, vcID.String(), credentials["1"].ID.String())
})
t.Run("ok - 2 presentations", func(t *testing.T) {
constant1 := vcID.String()
secondVCID := ssi.MustParseURI("did:example:123#second-vc")
constant2 := secondVCID.String()
secondVP := vc.VerifiablePresentation{
VerifiableCredential: []vc.VerifiableCredential{
{ID: &secondVCID},
},
Proof: []interface{}{
proof.LDProof{VerificationMethod: vcID},
},
}
definition := PresentationDefinition{
InputDescriptors: []*InputDescriptor{
{
Id: "1",
Constraints: &Constraints{
Fields: []Field{
{
Path: []string{"$.id"},
Filter: &Filter{
Type: "string",
Const: &constant1,
},
},
},
},
},
{
Id: "2",
Constraints: &Constraints{
Fields: []Field{
{
Path: []string{"$.id"},
Filter: &Filter{
Type: "string",
Const: &constant2,
},
},
},
},
},
},
}
submission := PresentationSubmission{
DescriptorMap: []InputDescriptorMappingObject{
{
Id: "1",
Path: "$.verifiableCredential[0]",
},
{
Id: "2",
Path: "$.verifiableCredential[1]",
},
},
}

credentials, err := submission.Validate([]vc.VerifiablePresentation{vp, secondVP}, definition)

assert.NoError(t, err, "credential submission is invalid, credentials does not match the presentation definition")
assert.Len(t, credentials, 1)
Expand All @@ -151,7 +218,7 @@ func TestPresentationSubmission_Validate(t *testing.T) {
},
}

credentials, err := PresentationSubmission{}.Validate(vp, definition)
credentials, err := PresentationSubmission{}.Validate([]vc.VerifiablePresentation{vp}, definition)

assert.EqualError(t, err, "credential submission is invalid, credentials do not match the presentation definition")
assert.Empty(t, credentials)
Expand Down Expand Up @@ -210,7 +277,7 @@ func TestPresentationSubmission_Validate(t *testing.T) {
},
}

credentials, err := submission.Validate(vp, definition)
credentials, err := submission.Validate([]vc.VerifiablePresentation{vp}, definition)

assert.EqualError(t, err, "credential submission is invalid, input descriptor mapping looks invalid")
assert.Empty(t, credentials)
Expand Down

0 comments on commit 4a90357

Please sign in to comment.