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

Commit

Permalink
feat: change DI VM purpose resolution
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <mykhailo.sizov@securekey.com>
  • Loading branch information
mishasizov-SK committed Sep 1, 2023
1 parent 717faac commit ca15b9f
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 181 deletions.
64 changes: 30 additions & 34 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 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
116 changes: 107 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,102 @@ 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
}

0 comments on commit ca15b9f

Please sign in to comment.