Skip to content

Commit

Permalink
feat: vc wallet prove interface
Browse files Browse the repository at this point in the history
- added prove interface based on universal wallet prove interface
(https://w3c-ccg.github.io/universal-wallet-interop-spec/#prove).
- support for both stored and raw credential arguments.
- Closes hyperledger-archives#2680

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Mar 25, 2021
1 parent ad29113 commit e582a7b
Show file tree
Hide file tree
Showing 4 changed files with 547 additions and 38 deletions.
15 changes: 10 additions & 5 deletions pkg/client/vcwallet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,17 @@ func (c *Client) Issue(credential json.RawMessage,
// Prove produces a Verifiable Presentation.
//
// Args:
// - List of verifiable credentials IDs.
// - Proof options
// - list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or
// raw credential).
// - proof options
//
func (c *Client) Prove(credentialIDs []string, options *wallet.ProofOptions) (json.RawMessage, error) {
// TODO to be added #2433
return nil, fmt.Errorf("to be implemented")
func (c *Client) Prove(credentials []interface{}, options *wallet.ProofOptions) (*verifiable.Presentation, error) {
auth, err := c.auth()
if err != nil {
return nil, err
}

return c.wallet.Prove(auth, credentials, options)
}

// Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,.
Expand Down
67 changes: 59 additions & 8 deletions pkg/client/vcwallet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,18 +612,69 @@ func TestClient_Issue(t *testing.T) {
}

func TestClient_Prove(t *testing.T) {
customVDR := &mockvdr.MockVDRegistry{
ResolveFunc: func(didID string, opts ...vdrapi.ResolveOption) (*did.DocResolution, error) {
if strings.HasPrefix(didID, "did:key:") {
k := key.New()

d, e := k.Read(didID)
if e != nil {
return nil, e
}

return d, nil
}

return nil, fmt.Errorf("did not found")
},
}

mockctx := newMockProvider()
err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
require.NoError(t, err)
mockctx.VDRegistryValue = customVDR
mockctx.CryptoValue = &cryptomock.Crypto{}

vcWalletClient, err := New(sampleUserID, mockctx)
require.NotEmpty(t, vcWalletClient)
err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
require.NoError(t, err)

result, err := vcWalletClient.Prove(nil, &wallet.ProofOptions{})
require.Empty(t, result)
require.Error(t, err)
require.EqualError(t, err, toBeImplementedErr)
t.Run("Test VC wallet client prove using controller - failure", func(t *testing.T) {
vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
require.NotEmpty(t, vcWalletClient)
require.NoError(t, err)

defer vcWalletClient.Close()

require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(sampleUDCVC)))

result, err := vcWalletClient.Prove([]interface{}{
"http://example.edu/credentials/1872",
[]byte(sampleUDCVC),
}, &wallet.ProofOptions{
Controller: sampleDIDKey,
})
require.Error(t, err)
require.Contains(t, err.Error(), "failed to read json keyset from reader")
require.Empty(t, result)
})

t.Run("Test VC wallet client prove using controller - wallet locked", func(t *testing.T) {
vcWalletClient, err := New(sampleUserID, mockctx)
require.NotEmpty(t, vcWalletClient)
require.NoError(t, err)

defer vcWalletClient.Close()

require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(sampleUDCVC)))

result, err := vcWalletClient.Prove([]interface{}{
"http://example.edu/credentials/1872",
[]byte(sampleUDCVC),
}, &wallet.ProofOptions{
Controller: sampleDIDKey,
})
require.Error(t, err)
require.True(t, errors.Is(err, ErrWalletLocked))
require.Empty(t, result)
})
}

func TestClient_Verify(t *testing.T) {
Expand Down
80 changes: 73 additions & 7 deletions pkg/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,9 @@ func (c *Wallet) Query(query *QueryParams) ([]json.RawMessage, error) {
// Issue adds proof to a Verifiable Credential.
//
// Args:
// - A verifiable credential with or without proof
// - Proof options
// - auth token for unlocking kms.
// - A verifiable credential with or without proof.
// - Proof options.
//
func (c *Wallet) Issue(authToken string, credential json.RawMessage,
options *ProofOptions) (*verifiable.Credential, error) {
Expand All @@ -387,12 +388,37 @@ func (c *Wallet) Issue(authToken string, credential json.RawMessage,
// Prove produces a Verifiable Presentation.
//
// Args:
// - List of verifiable credentials IDs.
// - Proof options
// - auth token for unlocking kms.
// - list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or
// raw credential).
// - proof options
//
func (c *Wallet) Prove(credentialIDs []string, options *ProofOptions) (json.RawMessage, error) {
// TODO to be added #2433
return nil, fmt.Errorf("to be implemented")
func (c *Wallet) Prove(authToken string, credentials []interface{}, options *ProofOptions) (*verifiable.Presentation, error) { //nolint: lll
resolved, err := c.resolveCredentials(credentials)
if err != nil {
return nil, fmt.Errorf("failed to resolve credentials from request: %w", err)
}

purpose := did.Authentication

err = c.validateProofOption(options, purpose)
if err != nil {
return nil, fmt.Errorf("failed to prepare proof: %w", err)
}

presentation, err := verifiable.NewPresentation(verifiable.WithCredentials(resolved...))
if err != nil {
return nil, fmt.Errorf("failed to prepare presentation: %w", err)
}

presentation.Holder = options.Controller

err = c.addLinkedDataProof(authToken, presentation, options, purpose)
if err != nil {
return nil, fmt.Errorf("failed to prove credentials: %w", err)
}

return presentation, nil
}

// Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,.
Expand All @@ -406,6 +432,46 @@ func (c *Wallet) Verify(raw json.RawMessage) (bool, error) {
return false, fmt.Errorf("to be implemented")
}

func (c *Wallet) resolveCredentials(credentials []interface{}) ([]*verifiable.Credential, error) {
var response []*verifiable.Credential

for _, cred := range credentials {
switch v := cred.(type) {
case string:
raw, err := c.contents.Get(Credential, v)
if err != nil {
return nil, err
}

credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck())
if err != nil {
return nil, err
}

response = append(response, credential)

case []byte:
credential, err := verifiable.ParseCredential(v, verifiable.WithDisabledProofCheck())
if err != nil {
return nil, err
}

response = append(response, credential)
case json.RawMessage:
credential, err := verifiable.ParseCredential(v, verifiable.WithDisabledProofCheck())
if err != nil {
return nil, err
}

response = append(response, credential)
default:
return nil, errors.New("unexpected credential type, supported types - string, raw")
}
}

return response, nil
}

func (c *Wallet) addLinkedDataProof(authToken string, p provable, opts *ProofOptions,
relationship did.VerificationRelationship) error {
s, err := newKMSSigner(authToken, c.ctx.Crypto(), opts)
Expand Down

0 comments on commit e582a7b

Please sign in to comment.