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

Commit

Permalink
feat: vc wallet prove interface (#2681)
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 #2680

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Mar 25, 2021
1 parent ad29113 commit a5b4943
Show file tree
Hide file tree
Showing 4 changed files with 535 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(opts *wallet.ProofOptions, creds ...wallet.CredentialToProve) (*verifiable.Presentation, error) {
auth, err := c.auth()
if err != nil {
return nil, err
}

return c.wallet.Prove(auth, opts, creds...)
}

// Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,.
Expand Down
63 changes: 55 additions & 8 deletions pkg/client/vcwallet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,18 +612,65 @@ 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(&wallet.ProofOptions{Controller: sampleDIDKey},
wallet.WithStoredCredentials("http://example.edu/credentials/1872"),
wallet.WithRawCredentials([]byte(sampleUDCVC)),
)
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(&wallet.ProofOptions{Controller: sampleDIDKey},
wallet.WithStoredCredentials("http://example.edu/credentials/1872"),
wallet.WithRawCredentials([]byte(sampleUDCVC)),
)
require.Error(t, err)
require.True(t, errors.Is(err, ErrWalletLocked))
require.Empty(t, result)
})
}

func TestClient_Verify(t *testing.T) {
Expand Down
100 changes: 93 additions & 7 deletions pkg/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,31 @@ func WithUnlockExpiry(tokenExpiry time.Duration) UnlockOptions {
}
}

// proveOpts contains options for proving credentials.
type proveOpts struct {
// credentials already saved in wallet.
storedCredentials []string
// credential being supplied to wallet to prove.
rawCredentials []json.RawMessage
}

// CredentialToProve options for proving credential to prove from wallet.
type CredentialToProve func(opts *proveOpts)

// WithStoredCredentials option for providing stored credential IDs for wallet to prove.
func WithStoredCredentials(ids ...string) CredentialToProve {
return func(opts *proveOpts) {
opts.storedCredentials = ids
}
}

// WithRawCredentials option for providing raw credential for wallet to prove.
func WithRawCredentials(raw ...json.RawMessage) CredentialToProve {
return func(opts *proveOpts) {
opts.rawCredentials = raw
}
}

// Wallet enables access to verifiable credential wallet features.
type Wallet struct {
// ID of wallet content owner
Expand Down Expand Up @@ -359,8 +384,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 +413,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, proofOptions *ProofOptions, credentials ...CredentialToProve) (*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(proofOptions, 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 = proofOptions.Controller

err = c.addLinkedDataProof(authToken, presentation, proofOptions, 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 +457,41 @@ func (c *Wallet) Verify(raw json.RawMessage) (bool, error) {
return false, fmt.Errorf("to be implemented")
}

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

opts := &proveOpts{}

for _, opt := range credentials {
opt(opts)
}

for _, id := range opts.storedCredentials {
raw, err := c.contents.Get(Credential, id)
if err != nil {
return nil, err
}

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

response = append(response, credential)
}

for _, raw := range opts.rawCredentials {
credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck())
if err != nil {
return nil, err
}

response = append(response, credential)
}

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 a5b4943

Please sign in to comment.