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

feat: change DI VM purpose resolution #3630

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 43 additions & 49 deletions component/models/dataintegrity/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ func TestIntegration(t *testing.T) {
p384JWK, err := jwkkid.BuildJWK(p384Bytes, kmsapi.ECDSAP384IEEEP1363)
require.NoError(t, err)

p256VM, err := did.NewVerificationMethodFromJWK(mockVMID, "JsonWebKey2020", mockDID, p256JWK)
p256VM, err := did.NewVerificationMethodFromJWK(mockKID, "JsonWebKey2020", mockDID, p256JWK)
require.NoError(t, err)

p384VM, err := did.NewVerificationMethodFromJWK(mockVMID2, "JsonWebKey2020", mockDID2, p384JWK)
p384VM, err := did.NewVerificationMethodFromJWK(mockKID2, "JsonWebKey2020", mockDID2, p384JWK)
require.NoError(t, err)

resolver := resolveFunc(func(id string) (*did.DocResolution, error) {
Expand All @@ -105,14 +105,13 @@ func TestIntegration(t *testing.T) {
t.Run("success", func(t *testing.T) {
t.Run("P-256 key", func(t *testing.T) {
signOpts := &models.ProofOptions{
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
}

signedCred, err := signer.AddProof(validCredential, signOpts)
Expand All @@ -121,7 +120,7 @@ func TestIntegration(t *testing.T) {
verifyOpts := &models.ProofOptions{
VerificationMethodID: mockKID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
Expand All @@ -133,14 +132,13 @@ func TestIntegration(t *testing.T) {

t.Run("P-384 key", func(t *testing.T) {
signOpts := &models.ProofOptions{
VerificationMethod: p384VM,
VerificationMethodID: mockKID2,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
VerificationMethod: p384VM,
VerificationMethodID: mockKID2,
SuiteType: ecdsa2019.SuiteType,
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
}

signedCred, err := signer.AddProof(validCredential, signOpts)
Expand All @@ -149,7 +147,7 @@ func TestIntegration(t *testing.T) {
verifyOpts := &models.ProofOptions{
VerificationMethodID: mockKID2,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
Created: time.Now(),
MaxAge: 100,
Expand All @@ -163,23 +161,21 @@ func TestIntegration(t *testing.T) {
t.Run("failure", func(t *testing.T) {
t.Run("wrong key", func(t *testing.T) {
signOpts := &models.ProofOptions{
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
Created: time.Now(),
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
Created: time.Now(),
}

verifyOpts := &models.ProofOptions{
VerificationMethod: p384VM,
VerificationMethodID: p384VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
MaxAge: 100,
VerificationMethod: p384VM,
VerificationMethodID: p384VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: AssertionMethod,
ProofType: models.DataIntegrityProof,
MaxAge: 100,
}

signedCred, err := signer.AddProof(validCredential, signOpts)
Expand All @@ -191,24 +187,22 @@ func TestIntegration(t *testing.T) {
})
t.Run("malformed proof created", func(t *testing.T) {
signOpts := &models.ProofOptions{
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
Created: time.Now(),
VerificationMethod: p256VM,
VerificationMethodID: p256VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
ProofType: models.DataIntegrityProof,
Created: time.Now(),
}

verifyOpts := &models.ProofOptions{
VerificationMethod: p384VM,
VerificationMethodID: p384VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
VerificationRelationship: "assertionMethod",
ProofType: models.DataIntegrityProof,
MaxAge: 100,
Created: time.Time{},
VerificationMethod: p384VM,
VerificationMethodID: p384VM.ID,
SuiteType: ecdsa2019.SuiteType,
Purpose: "assertionMethod",
ProofType: models.DataIntegrityProof,
MaxAge: 100,
Created: time.Time{},
}

signedCred, err := signer.AddProof(validCredential, signOpts)
Expand Down
21 changes: 10 additions & 11 deletions component/models/dataintegrity/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,16 @@ type Proof struct {

// ProofOptions provides options for signing or verifying a data integrity proof.
type ProofOptions struct {
Purpose string
VerificationMethodID string
VerificationMethod *VerificationMethod
VerificationRelationship string
ProofType string
SuiteType string
Domain string
Challenge string
Created time.Time
MaxAge int64
CustomFields map[string]interface{}
Purpose string
VerificationMethodID string
VerificationMethod *VerificationMethod
ProofType string
SuiteType string
Domain string
Challenge string
Created time.Time
MaxAge int64
CustomFields map[string]interface{}
}

// DateTimeFormat is the date-time format used by the data integrity
Expand Down
117 changes: 108 additions & 9 deletions component/models/dataintegrity/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,31 @@
import (
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/tidwall/sjson"

"github.com/hyperledger/aries-framework-go/component/models/jwt/didsignjwt"

"github.com/hyperledger/aries-framework-go/component/models/dataintegrity/models"
"github.com/hyperledger/aries-framework-go/component/models/dataintegrity/suite"
"github.com/hyperledger/aries-framework-go/component/models/did"
)

const (
// AssertionMethod assertionMethod.
AssertionMethod = "assertionMethod"

// Authentication authentication.
Authentication = "authentication"

// CapabilityDelegation capabilityDelegation.
CapabilityDelegation = "capabilityDelegation"

// CapabilityInvocation capabilityInvocation.
CapabilityInvocation = "capabilityInvocation"

creatorParts = 2
invalidFormatErrMsgFmt = "verificationMethod value %s should be in did#keyID format"
)

// Signer implements the Add Proof algorithm of the verifiable credential data
Expand Down Expand Up @@ -117,7 +135,7 @@
}

func resolveVM(opts *models.ProofOptions, resolver didResolver, vmID string) error {
if opts.VerificationMethod == nil || opts.VerificationRelationship == "" {
if opts.VerificationMethod == nil {
if opts.VerificationMethodID == "" {
opts.VerificationMethodID = vmID
}
Expand All @@ -126,22 +144,103 @@
return ErrNoResolver
}

vm, vmID, rel, err := didsignjwt.ResolveSigningVMWithRelationship(opts.VerificationMethodID, resolver)
didDoc, err := getDIDDocFromVerificationMethod(opts.VerificationMethodID, resolver)
if err != nil {
// TODO update linter to use go 1.20: https://github.com/hyperledger/aries-framework-go/issues/3613
return errors.Join(ErrVMResolution, err) // nolint:typecheck
}

vm, err := getVMByPurpose(opts.Purpose, opts.VerificationMethodID, didDoc)
if err != nil {
// TODO update linter to use go 1.20: https://github.com/hyperledger/aries-framework-go/issues/3613
return errors.Join(ErrVMResolution, err) // nolint:typecheck
}

opts.VerificationMethodID = vmID
opts.VerificationMethod = vm
}

return nil
}

func getVMByPurpose(purpose, vmID string, didDoc *did.Doc) (*did.VerificationMethod, error) {
var verificationMethod *did.VerificationMethod

vmIDFragment := vmIDFragmentOnly(vmID)

// A VM with general relationship is allowed for assertion
if rel == "" {
rel = "assertionMethod"
switch purpose {
case AssertionMethod:
assertionMethods := didDoc.VerificationMethods(did.AssertionMethod)[did.AssertionMethod]

verificationMethod = getVM(vmIDFragment, assertionMethods)
if verificationMethod == nil {
// A VM with general relationship is allowed for assertion
generalMethods :=
didDoc.VerificationMethods(did.VerificationRelationshipGeneral)[did.VerificationRelationshipGeneral]

verificationMethod = getVM(vmIDFragment, generalMethods)
}
case Authentication:
authMethods := didDoc.VerificationMethods(did.Authentication)[did.Authentication]

verificationMethod = getVM(vmIDFragment, authMethods)
case CapabilityDelegation:
capabilityDelegationMethods := didDoc.VerificationMethods(did.CapabilityDelegation)[did.CapabilityDelegation]

verificationMethod = getVM(vmIDFragment, capabilityDelegationMethods)
case CapabilityInvocation:
capabilityInvocationMethods := didDoc.VerificationMethods(did.CapabilityInvocation)[did.CapabilityInvocation]

verificationMethod = getVM(vmIDFragment, capabilityInvocationMethods)
default:
return nil, fmt.Errorf("purpose %s not supported", purpose)
}

opts.VerificationRelationship = rel
if verificationMethod == nil {
return nil, fmt.Errorf("unable to find matching %s key IDs for given verification method ID %s",
purpose, vmID)
}

Check warning on line 201 in component/models/dataintegrity/signer.go

View check run for this annotation

Codecov / codecov/patch

component/models/dataintegrity/signer.go#L199-L201

Added lines #L199 - L201 were not covered by tests

return verificationMethod, nil
}

func getVM(vmID string, vms []did.Verification) *did.VerificationMethod {
for _, verification := range vms {
if vmID == vmIDFragmentOnly(verification.VerificationMethod.ID) {
return &verification.VerificationMethod
}
}

return nil
}

func vmIDFragmentOnly(vmID string) string {
vmSplit := strings.Split(vmID, "#")
if len(vmSplit) == 1 {
return vmSplit[0]
}

Check warning on line 220 in component/models/dataintegrity/signer.go

View check run for this annotation

Codecov / codecov/patch

component/models/dataintegrity/signer.go#L219-L220

Added lines #L219 - L220 were not covered by tests

return vmSplit[1]
}

func getDIDDocFromVerificationMethod(verificationMethod string, didResolver didResolver) (*did.Doc, error) {
didID, err := getDIDFromVerificationMethod(verificationMethod)
if err != nil {
return nil, err
}

Check warning on line 229 in component/models/dataintegrity/signer.go

View check run for this annotation

Codecov / codecov/patch

component/models/dataintegrity/signer.go#L228-L229

Added lines #L228 - L229 were not covered by tests

docResolution, err := didResolver.Resolve(didID)
if err != nil {
return nil, err
}

return docResolution.DIDDocument, nil
}

func getDIDFromVerificationMethod(creator string) (string, error) {
idSplit := strings.Split(creator, "#")
if len(idSplit) != creatorParts {
return "", fmt.Errorf(fmt.Sprintf(invalidFormatErrMsgFmt, creator))
}

Check warning on line 243 in component/models/dataintegrity/signer.go

View check run for this annotation

Codecov / codecov/patch

component/models/dataintegrity/signer.go#L242-L243

Added lines #L242 - L243 were not covered by tests

return idSplit[0], nil
}