github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/vcwallet/client.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package vcwallet 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 14 "github.com/piprate/json-gold/ld" 15 16 "github.com/hyperledger/aries-framework-go/pkg/client/outofband" 17 "github.com/hyperledger/aries-framework-go/pkg/common/log" 18 "github.com/hyperledger/aries-framework-go/pkg/crypto" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/cm" 21 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 22 "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 23 "github.com/hyperledger/aries-framework-go/pkg/kms" 24 "github.com/hyperledger/aries-framework-go/pkg/wallet" 25 "github.com/hyperledger/aries-framework-go/spi/storage" 26 ) 27 28 var logger = log.New("aries-framework/client/vcwallet") 29 30 // ErrWalletLocked when key manager related operation attempted on locked wallet. 31 var ErrWalletLocked = errors.New("wallet locked") 32 33 // provider contains dependencies for the verifiable credential wallet client 34 // and is typically created by using aries.Context(). 35 type provider interface { 36 StorageProvider() storage.Provider 37 VDRegistry() vdr.Registry 38 Crypto() crypto.Crypto 39 JSONLDDocumentLoader() ld.DocumentLoader 40 MediaTypeProfiles() []string 41 didCommProvider // to be used only if wallet needs to be participated in DIDComm. 42 } 43 44 // didCommProvider to be used only if wallet needs to be participated in DIDComm operation. 45 // TODO: using wallet KMS instead of provider KMS. 46 // TODO: reconcile Protocol storage with wallet store. 47 type didCommProvider interface { 48 KMS() kms.KeyManager 49 ServiceEndpoint() string 50 ProtocolStateStorageProvider() storage.Provider 51 Service(id string) (interface{}, error) 52 KeyType() kms.KeyType 53 KeyAgreementType() kms.KeyType 54 } 55 56 // walletAuth is auth function which returns wallet unlock token. 57 type walletAuth func() (string, error) 58 59 // noAuth default auth when wallet is still locked. 60 // nolint:gochecknoglobals 61 var noAuth walletAuth = func() (string, error) { return "", ErrWalletLocked } 62 63 // Client enable access to verifiable credential wallet features. 64 type Client struct { 65 wallet *wallet.Wallet 66 didComm *wallet.DidComm 67 auth walletAuth 68 } 69 70 // New returns new verifiable credential wallet client for given user. 71 // 72 // Args: 73 // - userID : unique user identifier used for login. 74 // - provider: dependencies for the verifiable credential wallet client. 75 // - options : options for unlocking wallet. Any other existing wallet instance of same wallet user will be locked 76 // once this instance is unlocked. 77 // 78 // returns error if wallet profile is not found. 79 // To create a new wallet profile, use `CreateProfile()`. 80 // To update an existing profile, use `UpdateProfile()`. 81 func New(userID string, ctx provider, options ...wallet.UnlockOptions) (*Client, error) { 82 w, err := wallet.New(userID, ctx) 83 if err != nil { 84 return nil, err 85 } 86 87 didComm, err := wallet.NewDidComm(w, ctx) 88 if err != nil { 89 return nil, err 90 } 91 92 client := &Client{wallet: w, didComm: didComm, auth: noAuth} 93 94 if len(options) > 0 { 95 if client.Close() { 96 logger.Debugf("wallet was already open, existing wallet instance key manager is now closed") 97 } 98 99 err = client.Open(options...) 100 if err != nil { 101 return nil, err 102 } 103 } 104 105 return client, nil 106 } 107 108 // CreateProfile creates a new verifiable credential wallet profile for given user. 109 // returns error if wallet profile is already created. 110 // Use `UpdateProfile()` for replacing an already created verifiable credential wallet profile. 111 func CreateProfile(userID string, ctx provider, options ...wallet.ProfileOptions) error { 112 return wallet.CreateProfile(userID, ctx, options...) 113 } 114 115 // UpdateProfile updates existing verifiable credential wallet profile. 116 // Will create new profile if no profile exists for given user. 117 // Caution: you might lose your existing keys if you change kms options. 118 func UpdateProfile(userID string, ctx provider, options ...wallet.ProfileOptions) error { 119 return wallet.UpdateProfile(userID, ctx, options...) 120 } 121 122 // ProfileExists checks if profile exists for given wallet user, returns error if not found. 123 func ProfileExists(userID string, ctx provider) error { 124 return wallet.ProfileExists(userID, ctx) 125 } 126 127 // Open unlocks wallet client's key manager instance and returns a token for subsequent use of wallet features. 128 // 129 // Args: 130 // - unlock options for opening wallet. 131 // 132 // Returns token with expiry that can be used for subsequent use of wallet features. 133 func (c *Client) Open(options ...wallet.UnlockOptions) error { 134 authToken, err := c.wallet.Open(options...) 135 if err != nil { 136 return err 137 } 138 139 c.auth = func() (s string, e error) { 140 return authToken, nil 141 } 142 143 return nil 144 } 145 146 // Close expires token issued to this VC wallet client. 147 // returns false if token is not found or already expired for this wallet user. 148 func (c *Client) Close() bool { 149 c.auth = noAuth 150 151 return c.wallet.Close() 152 } 153 154 // Export produces a serialized exported wallet representation. 155 // Only ciphertext wallet contents can be exported. 156 // 157 // Args: 158 // - auth: token to be used to lock the wallet before exporting. 159 // 160 // Returns exported locked wallet. 161 // 162 // Supported data models: 163 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 164 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 165 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 166 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 167 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 168 // 169 func (c *Client) Export(auth string) (json.RawMessage, error) { 170 // TODO to be added #2433 171 return nil, fmt.Errorf("to be implemented") 172 } 173 174 // Import Takes a serialized exported wallet representation as input 175 // and imports all contents into wallet. 176 // 177 // Args: 178 // - contents: wallet content to be imported. 179 // - auth: token used while exporting the wallet. 180 // 181 // Supported data models: 182 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 183 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 184 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 185 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 186 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 187 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key 188 // 189 func (c *Client) Import(auth string, contents json.RawMessage) error { 190 // TODO to be added #2433 191 return fmt.Errorf("to be implemented") 192 } 193 194 // Add adds given data model to wallet contents store. 195 // 196 // Supported data models: 197 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 198 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 199 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 200 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 201 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 202 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key 203 // 204 // TODO: (#2433) support for correlation between wallet contents (ex: credentials to a profile/collection). 205 func (c *Client) Add(contentType wallet.ContentType, content json.RawMessage, options ...wallet.AddContentOptions) error { //nolint: lll 206 auth, err := c.auth() 207 if err != nil { 208 return err 209 } 210 211 return c.wallet.Add(auth, contentType, content, options...) 212 } 213 214 // Remove removes wallet content by content ID. 215 // 216 // Supported data models: 217 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 218 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 219 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 220 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 221 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 222 // 223 func (c *Client) Remove(contentType wallet.ContentType, contentID string) error { 224 auth, err := c.auth() 225 if err != nil { 226 return err 227 } 228 229 return c.wallet.Remove(auth, contentType, contentID) 230 } 231 232 // Get fetches a wallet content by content ID. 233 // 234 // Supported data models: 235 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 236 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 237 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 238 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 239 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 240 // 241 func (c *Client) Get(contentType wallet.ContentType, contentID string) (json.RawMessage, error) { 242 auth, err := c.auth() 243 if err != nil { 244 return nil, err 245 } 246 247 return c.wallet.Get(auth, contentType, contentID) 248 } 249 250 // GetAll fetches all wallet contents of given type. 251 // 252 // Supported data models: 253 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 254 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 255 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 256 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 257 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 258 // 259 func (c *Client) GetAll(contentType wallet.ContentType, options ...wallet.GetAllContentsOptions) (map[string]json.RawMessage, error) { //nolint: lll 260 auth, err := c.auth() 261 if err != nil { 262 return nil, err 263 } 264 265 return c.wallet.GetAll(auth, contentType, options...) 266 } 267 268 // Query runs query against wallet credential contents and returns presentation containing credential results. 269 // 270 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#query 271 // 272 // Supported Query Types: 273 // - https://www.w3.org/TR/json-ld11-framing 274 // - https://identity.foundation/presentation-exchange 275 // - https://w3c-ccg.github.io/vp-request-spec/#query-by-example 276 // 277 func (c *Client) Query(params ...*wallet.QueryParams) ([]*verifiable.Presentation, error) { 278 auth, err := c.auth() 279 if err != nil { 280 return nil, err 281 } 282 283 return c.wallet.Query(auth, params...) 284 } 285 286 // Issue adds proof to a Verifiable Credential. 287 // 288 // Args: 289 // - A verifiable credential with or without proof 290 // - Proof options 291 // 292 func (c *Client) Issue(credential json.RawMessage, 293 options *wallet.ProofOptions) (*verifiable.Credential, error) { 294 auth, err := c.auth() 295 if err != nil { 296 return nil, err 297 } 298 299 return c.wallet.Issue(auth, credential, options) 300 } 301 302 // Prove produces a Verifiable Presentation. 303 // 304 // Args: 305 // - list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or 306 // raw credential). 307 // - proof options 308 // 309 func (c *Client) Prove(opts *wallet.ProofOptions, creds ...wallet.ProveOptions) (*verifiable.Presentation, error) { //nolint: lll 310 auth, err := c.auth() 311 if err != nil { 312 return nil, err 313 } 314 315 return c.wallet.Prove(auth, opts, creds...) 316 } 317 318 // Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,. 319 // 320 // Args: 321 // - verification option for sending different models (stored credential ID, raw credential, raw presentation). 322 // 323 // Returns: a boolean verified, and an error if verified is false. 324 func (c *Client) Verify(option wallet.VerificationOption) (bool, error) { 325 auth, err := c.auth() 326 if err != nil { 327 return false, err 328 } 329 330 return c.wallet.Verify(auth, option) 331 } 332 333 // Derive derives a credential and returns response credential. 334 // 335 // Args: 336 // - credential to derive (ID of the stored credential, raw credential or credential instance). 337 // - derive options. 338 // 339 func (c *Client) Derive(credential wallet.CredentialToDerive, options *wallet.DeriveOptions) (*verifiable.Credential, error) { //nolint: lll 340 auth, err := c.auth() 341 if err != nil { 342 return nil, err 343 } 344 345 return c.wallet.Derive(auth, credential, options) 346 } 347 348 // CreateKeyPair creates key pair inside a wallet. 349 // 350 // Args: 351 // - authToken: authorization for performing create key pair operation. 352 // - keyType: type of the key to be created. 353 // 354 func (c *Client) CreateKeyPair(keyType kms.KeyType) (*wallet.KeyPair, error) { 355 auth, err := c.auth() 356 if err != nil { 357 return nil, err 358 } 359 360 return c.wallet.CreateKeyPair(auth, keyType) 361 } 362 363 // Connect accepts out-of-band invitations and performs DID exchange. 364 // 365 // Args: 366 // - invitation: out-of-band invitation. 367 // - options: connection options. 368 // 369 // Returns: 370 // - connection ID if DID exchange is successful. 371 // - error if operation false. 372 // 373 func (c *Client) Connect(invitation *outofband.Invitation, options ...wallet.ConnectOptions) (string, error) { 374 auth, err := c.auth() 375 if err != nil { 376 return "", err 377 } 378 379 return c.didComm.Connect(auth, invitation, options...) 380 } 381 382 // ProposePresentation accepts out-of-band invitation and sends message proposing presentation 383 // from wallet to relying party. 384 // 385 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation 386 // 387 // Currently Supporting 388 // [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) 389 // 390 // Args: 391 // - invitation: out-of-band invitation from relying party. 392 // - options: options for accepting invitation and send propose presentation message. 393 // 394 // Returns: 395 // - DIDCommMsgMap containing request presentation message if operation is successful. 396 // - error if operation fails. 397 // 398 func (c *Client) ProposePresentation(invitation *wallet.GenericInvitation, options ...wallet.InitiateInteractionOption) (*service.DIDCommMsgMap, error) { //nolint: lll 399 auth, err := c.auth() 400 if err != nil { 401 return nil, err 402 } 403 404 return c.didComm.ProposePresentation(auth, invitation, options...) 405 } 406 407 // PresentProof sends message present proof message from wallet to relying party. 408 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof 409 // 410 // Currently Supporting 411 // [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) 412 // 413 // Args: 414 // - thID: thread ID (action ID) of request presentation. 415 // - presentation: presentation to be sent. 416 // 417 // Returns: 418 // - Credential interaction status containing status, redirectURL. 419 // - error if operation fails. 420 // 421 func (c *Client) PresentProof(thID string, presentProofFrom ...wallet.ConcludeInteractionOptions) (*wallet.CredentialInteractionStatus, error) { //nolint: lll 422 auth, err := c.auth() 423 if err != nil { 424 return nil, err 425 } 426 427 return c.didComm.PresentProof(auth, thID, presentProofFrom...) 428 } 429 430 // ProposeCredential sends propose credential message from wallet to issuer. 431 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential 432 // 433 // Currently Supporting : 0453-issueCredentialV2 434 // https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md 435 // 436 // Args: 437 // - invitation: out-of-band invitation from issuer. 438 // - options: options for accepting invitation and send propose credential message. 439 // 440 // Returns: 441 // - DIDCommMsgMap containing offer credential message if operation is successful. 442 // - error if operation fails. 443 // 444 func (c *Client) ProposeCredential(invitation *wallet.GenericInvitation, options ...wallet.InitiateInteractionOption) (*service.DIDCommMsgMap, error) { // nolint: lll 445 auth, err := c.auth() 446 if err != nil { 447 return nil, err 448 } 449 450 return c.didComm.ProposeCredential(auth, invitation, options...) 451 } 452 453 // RequestCredential sends request credential message from wallet to issuer and 454 // optionally waits for credential response. 455 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential 456 // 457 // Currently Supporting : 0453-issueCredentialV2 458 // https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md 459 // 460 // Args: 461 // - thID: thread ID (action ID) of offer credential message previously received. 462 // - concludeInteractionOptions: options to conclude interaction like presentation to be shared etc. 463 // 464 // Returns: 465 // - Credential interaction status containing status, redirectURL. 466 // - error if operation fails. 467 // 468 func (c *Client) RequestCredential(thID string, options ...wallet.ConcludeInteractionOptions) (*wallet.CredentialInteractionStatus, error) { // nolint: lll 469 auth, err := c.auth() 470 if err != nil { 471 return nil, err 472 } 473 474 return c.didComm.RequestCredential(auth, thID, options...) 475 } 476 477 // ResolveCredentialManifest resolves given credential manifest by credential response or credential. 478 // Supports: https://identity.foundation/credential-manifest/ 479 // 480 // Args: 481 // - authToken: authorization for performing operation. 482 // - manifest: Credential manifest data model in raw format. 483 // - resolve: options to provide credential response or credential to resolve. 484 // 485 // Returns: 486 // - list of resolved descriptors. 487 // - error if operation fails. 488 // 489 func (c *Client) ResolveCredentialManifest(manifest json.RawMessage, resolve wallet.ResolveManifestOption) ([]*cm.ResolvedDescriptor, error) { // nolint: lll 490 auth, err := c.auth() 491 if err != nil { 492 return nil, err 493 } 494 495 return c.wallet.ResolveCredentialManifest(auth, manifest, resolve) 496 }