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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
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 @@ package dataintegrity
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 (s *Signer) AddProof(doc []byte, opts *models.ProofOptions) ([]byte, error)
}

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 @@ func resolveVM(opts *models.ProofOptions, resolver didResolver, vmID string) err
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)
}

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]
}

return vmSplit[1]
}

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

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))
}

return idSplit[0], nil
}
Loading