github.com/hyperledger/aries-framework-go@v0.3.2/pkg/wallet/wallet.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package wallet 8 9 import ( 10 "encoding/base64" 11 "encoding/json" 12 "errors" 13 "fmt" 14 15 "github.com/piprate/json-gold/ld" 16 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/doc/cm" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/did" 21 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld" 22 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer" 23 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite" 24 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020" 25 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018" 26 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020" 27 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 28 "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 29 "github.com/hyperledger/aries-framework-go/pkg/kms" 30 "github.com/hyperledger/aries-framework-go/spi/storage" 31 ) 32 33 // Proof types. 34 const ( 35 // Ed25519Signature2018 ed25519 signature suite. 36 Ed25519Signature2018 = "Ed25519Signature2018" 37 // JSONWebSignature2020 json web signature suite. 38 JSONWebSignature2020 = "JsonWebSignature2020" 39 // BbsBlsSignature2020 BBS signature suite. 40 BbsBlsSignature2020 = "BbsBlsSignature2020" 41 ) 42 43 // miscellaneous constants. 44 const ( 45 bbsContext = "https://w3id.org/security/bbs/v1" 46 emptyRawLength = 4 47 48 // web redirect constants. 49 webRedirectStatusKey = "status" 50 webRedirectURLKey = "url" 51 ) 52 53 // proof options. 54 // nolint:gochecknoglobals 55 var ( 56 defaultSignatureRepresentation = verifiable.SignatureJWS 57 supportedRelationships = map[did.VerificationRelationship]string{ 58 did.Authentication: "authentication", 59 did.AssertionMethod: "assertionMethod", 60 } 61 ) 62 63 var logger = log.New("aries-framework/wallet") 64 65 // provider contains dependencies for the verifiable credential wallet 66 // and is typically created by using aries.Context(). 67 type provider interface { 68 StorageProvider() storage.Provider 69 VDRegistry() vdr.Registry 70 Crypto() crypto.Crypto 71 JSONLDDocumentLoader() ld.DocumentLoader 72 MediaTypeProfiles() []string 73 } 74 75 type provable interface { 76 AddLinkedDataProof(context *verifiable.LinkedDataProofContext, jsonldOpts ...jsonld.ProcessorOpts) error 77 } 78 79 type jwtClaims interface { 80 MarshalJWS(signatureAlg verifiable.JWSAlgorithm, signer verifiable.Signer, keyID string) (string, error) 81 } 82 83 // Wallet enables access to verifiable credential wallet features. 84 type Wallet struct { 85 // ID of wallet content owner 86 userID string 87 88 // wallet profile 89 profile *profile 90 91 // wallet content store 92 contents *contentStore 93 94 // crypto for wallet 95 walletCrypto crypto.Crypto 96 97 // storage provider 98 storeProvider storage.Provider 99 100 // wallet VDR 101 vdr vdr.Registry 102 103 // document loader for JSON-LD contexts 104 jsonldDocumentLoader ld.DocumentLoader 105 } 106 107 // New returns new verifiable credential wallet for given user. 108 // returns error if wallet profile is not found. 109 // To create a new wallet profile, use `CreateProfile()`. 110 // To update an existing profile, use `UpdateProfile()`. 111 func New(userID string, ctx provider) (*Wallet, error) { 112 store, err := newProfileStore(ctx.StorageProvider()) 113 if err != nil { 114 return nil, fmt.Errorf("failed to get store to fetch VC wallet profile info: %w", err) 115 } 116 117 profile, err := store.get(userID) 118 if err != nil { 119 return nil, fmt.Errorf("failed to get VC wallet profile: %w", err) 120 } 121 122 return &Wallet{ 123 userID: userID, 124 profile: profile, 125 storeProvider: ctx.StorageProvider(), 126 walletCrypto: ctx.Crypto(), 127 contents: newContentStore(ctx.StorageProvider(), ctx.JSONLDDocumentLoader(), profile), 128 vdr: ctx.VDRegistry(), 129 jsonldDocumentLoader: ctx.JSONLDDocumentLoader(), 130 }, nil 131 } 132 133 // CreateProfile creates a new verifiable credential wallet profile for given user. 134 // returns error if wallet profile is already created. 135 // Use `UpdateProfile()` for replacing an already created verifiable credential wallet profile. 136 func CreateProfile(userID string, ctx provider, options ...ProfileOptions) error { 137 return createOrUpdate(userID, ctx, false, options...) 138 } 139 140 // UpdateProfile updates existing verifiable credential wallet profile. 141 // Caution: 142 // - you might lose your existing keys if you change kms options. 143 // - you might lose your existing wallet contents if you change storage/EDV options 144 // (ex: switching context storage provider or changing EDV settings). 145 func UpdateProfile(userID string, ctx provider, options ...ProfileOptions) error { 146 return createOrUpdate(userID, ctx, true, options...) 147 } 148 149 // CreateDataVaultKeyPairs can be used create EDV key pairs for given profile. 150 // Wallet will create key pairs in profile kms and updates profile with newly generate EDV encryption & MAC key IDs. 151 func CreateDataVaultKeyPairs(userID string, ctx provider, options ...UnlockOptions) error { 152 store, err := newProfileStore(ctx.StorageProvider()) 153 if err != nil { 154 return fmt.Errorf("failed to get wallet user profile: failed to get store: %w", err) 155 } 156 157 profile, err := store.get(userID) 158 if err != nil { 159 return fmt.Errorf("failed to get wallet user profile: %w", err) 160 } 161 162 if profile.EDVConf == nil { 163 return fmt.Errorf("invalid operation, no edv configuration found in profile: %w", err) 164 } 165 166 opts := &unlockOpts{} 167 168 for _, opt := range options { 169 opt(opts) 170 } 171 172 kmsStore, err := kms.NewAriesProviderWrapper(ctx.StorageProvider()) 173 if err != nil { 174 return err 175 } 176 177 // unlock key manager 178 kmsm, err := keyManager().createKeyManager(profile, kmsStore, opts) 179 if err != nil { 180 return fmt.Errorf("failed to get key manager: %w", err) 181 } 182 183 // update profile 184 err = updateProfile(kmsm, profile) 185 if err != nil { 186 return fmt.Errorf("failed to create key pairs: %w", err) 187 } 188 189 // update profile 190 err = store.save(profile, true) 191 if err != nil { 192 return fmt.Errorf("failed to update profile: %w", err) 193 } 194 195 return nil 196 } 197 198 func createOrUpdate(userID string, ctx provider, update bool, options ...ProfileOptions) error { 199 opts := &profileOpts{} 200 201 for _, opt := range options { 202 opt(opts) 203 } 204 205 store, err := newProfileStore(ctx.StorageProvider()) 206 if err != nil { 207 return fmt.Errorf("failed to get store to save VC wallet profile: %w", err) 208 } 209 210 var profile *profile 211 212 // nolint: nestif 213 if update { 214 // find existing profile and update it. 215 profile, err = store.get(userID) 216 if err != nil { 217 return fmt.Errorf("failed to update wallet user profile: %w", err) 218 } 219 220 err = profile.setKMSOptions(opts.passphrase, opts.secretLockSvc, opts.keyServerURL) 221 if err != nil { 222 return fmt.Errorf("failed to update wallet user profile KMS options: %w", err) 223 } 224 225 err = profile.setEDVOptions(opts.edvConf) 226 if err != nil { 227 return fmt.Errorf("failed to update EDV configuration") 228 } 229 } else { 230 // create new profile. 231 profile, err = createProfile(userID, opts) 232 if err != nil { 233 return fmt.Errorf("failed to create new wallet user profile: %w", err) 234 } 235 } 236 237 err = store.save(profile, update) 238 if err != nil { 239 return fmt.Errorf("failed to save VC wallet profile: %w", err) 240 } 241 242 return nil 243 } 244 245 // ProfileExists checks if profile exists for given wallet user, returns error if not found. 246 func ProfileExists(userID string, ctx provider) error { 247 store, err := newProfileStore(ctx.StorageProvider()) 248 if err != nil { 249 return fmt.Errorf("failed to get store to get VC wallet profile: %w", err) 250 } 251 252 _, err = store.get(userID) 253 254 return err 255 } 256 257 // Open unlocks wallet's key manager instance & open wallet content store and 258 // returns a token for subsequent use of wallet features. 259 // 260 // Args: 261 // - unlock options for opening wallet. 262 // 263 // Returns token with expiry that can be used for subsequent use of wallet features. 264 func (c *Wallet) Open(options ...UnlockOptions) (string, error) { 265 opts := &unlockOpts{} 266 267 for _, opt := range options { 268 opt(opts) 269 } 270 271 kmsStore, err := kms.NewAriesProviderWrapper(c.storeProvider) 272 if err != nil { 273 return "", err 274 } 275 276 // unlock key manager 277 keyManager, err := keyManager().createKeyManager(c.profile, kmsStore, opts) 278 if err != nil { 279 return "", err 280 } 281 282 token, err := sessionManager().createSession(c.profile.User, keyManager, opts.tokenExpiry) 283 if err != nil { 284 return "", err 285 } 286 287 // open content store using token 288 err = c.contents.Open(keyManager, opts) 289 if err != nil { 290 // close wallet if it fails to open store 291 c.Close() 292 293 return "", err 294 } 295 296 return token, nil 297 } 298 299 // Close expires token issued to this VC wallet, removes the key manager instance and closes wallet content store. 300 // returns false if token is not found or already expired for this wallet user. 301 func (c *Wallet) Close() bool { 302 return sessionManager().closeSession(c.userID) && c.contents.Close() 303 } 304 305 // Export produces a serialized exported wallet representation. 306 // Only ciphertext wallet contents can be exported. 307 // 308 // Args: 309 // - auth: token to be used to lock the wallet before exporting. 310 // 311 // Returns exported locked wallet. 312 // 313 // Supported data models: 314 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 315 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 316 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 317 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 318 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 319 func (c *Wallet) Export(auth string) (json.RawMessage, error) { 320 // TODO to be added #2433 321 return nil, fmt.Errorf("to be implemented") 322 } 323 324 // Import Takes a serialized exported wallet representation as input 325 // and imports all contents into wallet. 326 // 327 // Args: 328 // - contents: wallet content to be imported. 329 // - auth: token used while exporting the wallet. 330 // 331 // Supported data models: 332 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 333 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 334 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 335 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 336 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 337 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key 338 func (c *Wallet) Import(auth string, contents json.RawMessage) error { 339 // TODO to be added #2433 340 return fmt.Errorf("to be implemented") 341 } 342 343 // Add adds given data model to wallet contents store. 344 // 345 // Supported data models: 346 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 347 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 348 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 349 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 350 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 351 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key 352 func (c *Wallet) Add(authToken string, contentType ContentType, content json.RawMessage, options ...AddContentOptions) error { //nolint: lll 353 return c.contents.Save(authToken, contentType, content, options...) 354 } 355 356 // Remove removes wallet content by content ID. 357 // 358 // Supported data models: 359 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 360 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 361 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 362 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 363 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 364 func (c *Wallet) Remove(authToken string, contentType ContentType, contentID string) error { 365 return c.contents.Remove(authToken, contentID, contentType) 366 } 367 368 // Get fetches a wallet content by content ID. 369 // 370 // Supported data models: 371 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 372 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 373 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 374 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 375 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 376 func (c *Wallet) Get(authToken string, contentType ContentType, contentID string) (json.RawMessage, error) { 377 return c.contents.Get(authToken, contentID, contentType) 378 } 379 380 // GetAll fetches all wallet contents of given type. 381 // Returns map of key value from content store for given content type. 382 // 383 // Supported data models: 384 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection 385 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential 386 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse 387 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data 388 // - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection 389 func (c *Wallet) GetAll(authToken string, contentType ContentType, options ...GetAllContentsOptions) (map[string]json.RawMessage, error) { //nolint: lll 390 opts := &getAllContentsOpts{} 391 392 for _, option := range options { 393 option(opts) 394 } 395 396 if opts.collectionID != "" { 397 return c.contents.GetAllByCollection(authToken, opts.collectionID, contentType) 398 } 399 400 return c.contents.GetAll(authToken, contentType) 401 } 402 403 // Query runs query against wallet credential contents and returns presentation containing credential results. 404 // 405 // This function may return multiple presentations as query result based on combination of query types used. 406 // 407 // https://w3c-ccg.github.io/universal-wallet-interop-spec/#query 408 // 409 // Supported Query Types: 410 // - https://www.w3.org/TR/json-ld11-framing 411 // - https://identity.foundation/presentation-exchange 412 // - https://w3c-ccg.github.io/vp-request-spec/#query-by-example 413 // - https://w3c-ccg.github.io/vp-request-spec/#did-authentication-request 414 func (c *Wallet) Query(authToken string, params ...*QueryParams) ([]*verifiable.Presentation, error) { 415 vcContents, err := c.contents.GetAll(authToken, Credential) 416 if err != nil { 417 return nil, fmt.Errorf("failed to query credentials: %w", err) 418 } 419 420 query := NewQuery(verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(), 421 c.jsonldDocumentLoader, params...) 422 423 return query.PerformQuery(vcContents) 424 } 425 426 // Issue adds proof to a Verifiable Credential. 427 // 428 // Args: 429 // - auth token for unlocking kms. 430 // - A verifiable credential with or without proof. 431 // - Proof options. 432 func (c *Wallet) Issue(authToken string, credential json.RawMessage, 433 options *ProofOptions) (*verifiable.Credential, error) { 434 vc, err := verifiable.ParseCredential(credential, verifiable.WithDisabledProofCheck(), 435 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 436 if err != nil { 437 return nil, fmt.Errorf("failed to parse credential: %w", err) 438 } 439 440 purpose := did.AssertionMethod 441 442 err = c.validateProofOption(authToken, options, purpose) 443 if err != nil { 444 return nil, fmt.Errorf("failed to prepare proof: %w", err) 445 } 446 447 switch options.ProofFormat { 448 case ExternalJWTProofFormat: 449 claims, e := vc.JWTClaims(false) 450 if e != nil { 451 return nil, fmt.Errorf("failed to generate JWT claims for VC: %w", e) 452 } 453 454 jws, e := c.verifiableClaimsToJWT(authToken, claims, options) 455 if e != nil { 456 return nil, fmt.Errorf("failed to generate JWT VC: %w", e) 457 } 458 459 vc.JWT = jws 460 default: // default case is EmbeddedLDProofFormat 461 err = c.addLinkedDataProof(authToken, vc, options, purpose) 462 if err != nil { 463 return nil, fmt.Errorf("failed to issue credential: %w", err) 464 } 465 } 466 467 return vc, nil 468 } 469 470 // Prove produces a Verifiable Presentation. 471 // 472 // Args: 473 // - auth token for unlocking kms. 474 // - list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or 475 // raw credential or a presentation). 476 // - proof options 477 func (c *Wallet) Prove(authToken string, proofOptions *ProofOptions, credentials ...ProveOptions) (*verifiable.Presentation, error) { //nolint: lll 478 presentation, err := c.resolveOptionsToPresent(authToken, credentials...) 479 if err != nil { 480 return nil, fmt.Errorf("failed to resolve credentials from request: %w", err) 481 } 482 483 purpose := did.Authentication 484 485 err = c.validateProofOption(authToken, proofOptions, purpose) 486 if err != nil { 487 return nil, fmt.Errorf("failed to prepare proof: %w", err) 488 } 489 490 presentation.Holder = proofOptions.Controller 491 492 switch proofOptions.ProofFormat { 493 case ExternalJWTProofFormat: 494 // TODO: look into passing audience identifier 495 // https://github.com/hyperledger/aries-framework-go/issues/3354 496 claims, e := presentation.JWTClaims(nil, false) 497 if e != nil { 498 return nil, fmt.Errorf("failed to generate JWT claims for VP: %w", e) 499 } 500 501 jws, e := c.verifiableClaimsToJWT(authToken, claims, proofOptions) 502 if e != nil { 503 return nil, fmt.Errorf("failed to generate JWT VP: %w", e) 504 } 505 506 presentation.JWT = jws 507 default: // default case is EmbeddedLDProofFormat 508 err = c.addLinkedDataProof(authToken, presentation, proofOptions, purpose) 509 if err != nil { 510 return nil, fmt.Errorf("failed to prove credentials: %w", err) 511 } 512 } 513 514 return presentation, nil 515 } 516 517 // Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,. 518 // 519 // Args: 520 // - verification option for sending different models (stored credential ID, raw credential, raw presentation). 521 // 522 // Returns: a boolean verified, and an error if verified is false. 523 func (c *Wallet) Verify(authToken string, options VerificationOption) (bool, error) { 524 requestOpts := &verifyOpts{} 525 526 options(requestOpts) 527 528 switch { 529 case requestOpts.credentialID != "": 530 raw, err := c.contents.Get(authToken, requestOpts.credentialID, Credential) 531 if err != nil { 532 return false, fmt.Errorf("failed to get credential: %w", err) 533 } 534 535 return c.verifyCredential(authToken, raw) 536 case len(requestOpts.rawCredential) > 0: 537 return c.verifyCredential(authToken, requestOpts.rawCredential) 538 case len(requestOpts.rawPresentation) > 0: 539 return c.verifyPresentation(authToken, requestOpts.rawPresentation) 540 default: 541 return false, fmt.Errorf("invalid verify request") 542 } 543 } 544 545 // Derive derives a credential and returns response credential. 546 // 547 // Args: 548 // - credential to derive (ID of the stored credential, raw credential or credential instance). 549 // - derive options. 550 func (c *Wallet) Derive(authToken string, credential CredentialToDerive, options *DeriveOptions) (*verifiable.Credential, error) { //nolint: lll 551 vc, err := c.resolveCredentialToDerive(authToken, credential) 552 if err != nil { 553 return nil, fmt.Errorf("failed to resolve request : %w", err) 554 } 555 556 derived, err := vc.GenerateBBSSelectiveDisclosure(options.Frame, []byte(options.Nonce), 557 verifiable.WithPublicKeyFetcher( 558 verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(), 559 ), verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 560 if err != nil { 561 return nil, fmt.Errorf("failed to derive credential : %w", err) 562 } 563 564 return derived, nil 565 } 566 567 // CreateKeyPair creates key pair inside a wallet. 568 // 569 // Args: 570 // - authToken: authorization for performing create key pair operation. 571 // - keyType: type of the key to be created. 572 func (c *Wallet) CreateKeyPair(authToken string, keyType kms.KeyType) (*KeyPair, error) { 573 session, err := sessionManager().getSession(authToken) 574 if err != nil { 575 return nil, err 576 } 577 578 kid, pubBytes, err := session.KeyManager.CreateAndExportPubKeyBytes(keyType) 579 if err != nil { 580 return nil, err 581 } 582 583 return &KeyPair{ 584 KeyID: kid, 585 PublicKey: base64.RawURLEncoding.EncodeToString(pubBytes), 586 }, nil 587 } 588 589 // ResolveCredentialManifest resolves given credential manifest by credential response or credential. 590 // Supports: https://identity.foundation/credential-manifest/ 591 // 592 // Args: 593 // - authToken: authorization for performing operation. 594 // - manifest: Credential manifest data model in raw format. 595 // - resolve: options to provide credential response or credential to resolve. 596 // 597 // Returns: 598 // - list of resolved descriptors. 599 // - error if operation fails. 600 func (c *Wallet) ResolveCredentialManifest(authToken string, manifest json.RawMessage, resolve ResolveManifestOption) ([]*cm.ResolvedDescriptor, error) { //nolint: lll,gocyclo 601 credentialManifest := &cm.CredentialManifest{} 602 603 err := credentialManifest.UnmarshalJSON(manifest) 604 if err != nil { 605 return nil, fmt.Errorf("failed to read credential manifest: %w", err) 606 } 607 608 opts := &resolveManifestOpts{} 609 610 if resolve != nil { 611 resolve(opts) 612 } 613 614 switch { 615 case len(opts.rawResponse) > 0: 616 opts.response, err = verifiable.ParsePresentation(opts.rawResponse, 617 verifiable.WithPresDisabledProofCheck(), 618 verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader)) 619 if err != nil { 620 return nil, err 621 } 622 623 fallthrough 624 case opts.response != nil: 625 return credentialManifest.ResolveResponse(opts.response) 626 case opts.credentialID != "": 627 opts.rawCredential, err = c.Get(authToken, Credential, opts.credentialID) 628 if err != nil { 629 return nil, fmt.Errorf("failed to get credential to resolve from wallet: %w", err) 630 } 631 632 fallthrough 633 case len(opts.rawCredential) > 0: 634 opts.credential, err = verifiable.ParseCredential(opts.rawCredential, verifiable.WithDisabledProofCheck(), 635 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 636 if err != nil { 637 return nil, err 638 } 639 640 resolved, err := credentialManifest.ResolveCredential(opts.descriptorID, 641 cm.RawCredentialToResolve(opts.rawCredential)) 642 if err != nil { 643 return nil, fmt.Errorf("failed to resolve raw credential by descriptor ID '%s': %w", 644 opts.descriptorID, err) 645 } 646 647 return []*cm.ResolvedDescriptor{resolved}, nil 648 case opts.credential != nil: 649 resolved, err := credentialManifest.ResolveCredential(opts.descriptorID, 650 cm.CredentialToResolve(opts.credential)) 651 if err != nil { 652 return nil, fmt.Errorf("failed to resolve given credential by descriptor ID '%s' : %w", 653 opts.descriptorID, err) 654 } 655 656 return []*cm.ResolvedDescriptor{resolved}, nil 657 default: 658 return nil, errors.New("failed to resolve credential manifest, invalid option") 659 } 660 } 661 662 // nolint: funlen,gocyclo 663 func (c *Wallet) resolveOptionsToPresent(auth string, credentials ...ProveOptions) (*verifiable.Presentation, error) { 664 var allCredentials []*verifiable.Credential 665 666 opts := &proveOpts{} 667 668 for _, opt := range credentials { 669 opt(opts) 670 } 671 672 for _, id := range opts.storedCredentials { 673 raw, err := c.contents.Get(auth, id, Credential) 674 if err != nil { 675 return nil, err 676 } 677 678 // proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to 679 // show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it 680 // may to call 'wallet.Verify()' for each credential being presented. 681 // (More details can be found in issue #2677). 682 credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(), 683 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 684 if err != nil { 685 return nil, err 686 } 687 688 allCredentials = append(allCredentials, credential) 689 } 690 691 for _, raw := range opts.rawCredentials { 692 // proof check is disabled while resolving credentials from raw bytes. A wallet UI may or may not choose to 693 // show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it 694 // may to call 'wallet.Verify()' for each credential being presented. 695 // (More details can be found in issue #2677). 696 credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(), 697 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 698 if err != nil { 699 return nil, err 700 } 701 702 allCredentials = append(allCredentials, credential) 703 } 704 705 if len(opts.credentials) > 0 { 706 allCredentials = append(allCredentials, opts.credentials...) 707 } 708 709 if opts.presentation != nil { 710 opts.presentation.AddCredentials(allCredentials...) 711 712 return opts.presentation, nil 713 } else if len(opts.rawPresentation) > emptyRawLength { 714 vp, err := verifiable.ParsePresentation(opts.rawPresentation, verifiable.WithPresDisabledProofCheck(), 715 verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader)) 716 if err != nil { 717 return nil, err 718 } 719 720 vp.AddCredentials(allCredentials...) 721 722 return vp, nil 723 } 724 725 return verifiable.NewPresentation(verifiable.WithCredentials(allCredentials...)) 726 } 727 728 func (c *Wallet) resolveCredentialToDerive(auth string, credential CredentialToDerive) (*verifiable.Credential, error) { 729 opts := &deriveOpts{} 730 731 credential(opts) 732 733 if opts.credential != nil { 734 return opts.credential, nil 735 } 736 737 if len(opts.rawCredential) > 0 { 738 // proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to 739 // show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it 740 // may to call 'wallet.Verify()' for each credential being presented. 741 // (More details can be found in issue #2677). 742 return verifiable.ParseCredential(opts.rawCredential, verifiable.WithDisabledProofCheck(), 743 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 744 } 745 746 if opts.credentialID != "" { 747 raw, err := c.contents.Get(auth, opts.credentialID, Credential) 748 if err != nil { 749 return nil, err 750 } 751 752 // proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to 753 // show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it 754 // may to call 'wallet.Verify()' for each credential being presented. 755 // (More details can be found in issue #2677). 756 return verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(), 757 verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 758 } 759 760 return nil, errors.New("invalid request to derive credential") 761 } 762 763 func (c *Wallet) verifyCredential(authToken string, credential json.RawMessage) (bool, error) { 764 _, err := verifiable.ParseCredential(credential, verifiable.WithPublicKeyFetcher( 765 verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(), 766 ), verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader)) 767 if err != nil { 768 return false, fmt.Errorf("credential verification failed: %w", err) 769 } 770 771 return true, nil 772 } 773 774 func (c *Wallet) verifyPresentation(authToken string, presentation json.RawMessage) (bool, error) { 775 vp, err := verifiable.ParsePresentation(presentation, verifiable.WithPresPublicKeyFetcher( 776 verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(), 777 ), verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader)) 778 if err != nil { 779 return false, fmt.Errorf("presentation verification failed: %w", err) 780 } 781 782 // verify proof of each credential 783 for _, cred := range vp.Credentials() { 784 vc, err := json.Marshal(cred) 785 if err != nil { 786 return false, fmt.Errorf("failed to read credentials from presentation: %w", err) 787 } 788 789 _, err = c.verifyCredential(authToken, vc) 790 if err != nil { 791 return false, fmt.Errorf("presentation verification failed: %w", err) 792 } 793 } 794 795 return true, nil 796 } 797 798 func (c *Wallet) verifiableClaimsToJWT(authToken string, claims jwtClaims, options *ProofOptions) (string, error) { 799 s, err := newKMSSigner(authToken, c.walletCrypto, options) 800 if err != nil { 801 return "", fmt.Errorf("initializing signer: %w", err) 802 } 803 804 var alg verifiable.JWSAlgorithm 805 806 switch s.KeyType { 807 case kms.ED25519Type: 808 alg = verifiable.EdDSA 809 case kms.ECDSAP256TypeIEEEP1363: 810 alg = verifiable.ECDSASecp256r1 811 case kms.ECDSAP384TypeIEEEP1363: 812 alg = verifiable.ECDSASecp384r1 813 case kms.ECDSAP521TypeIEEEP1363: 814 alg = verifiable.ECDSASecp521r1 815 default: 816 return "", fmt.Errorf("unsupported keytype for JWT") 817 } 818 819 jws, err := claims.MarshalJWS(alg, s, options.VerificationMethod) 820 if err != nil { 821 return "", fmt.Errorf("failed to sign JWS: %w", err) 822 } 823 824 return jws, nil 825 } 826 827 func (c *Wallet) addLinkedDataProof(authToken string, p provable, opts *ProofOptions, 828 relationship did.VerificationRelationship) error { 829 s, err := newKMSSigner(authToken, c.walletCrypto, opts) 830 if err != nil { 831 return err 832 } 833 834 var signatureSuite signer.SignatureSuite 835 836 switch opts.ProofType { 837 case Ed25519Signature2018: 838 signatureSuite = ed25519signature2018.New(suite.WithSigner(s)) 839 case JSONWebSignature2020: 840 signatureSuite = jsonwebsignature2020.New(suite.WithSigner(s)) 841 case BbsBlsSignature2020: 842 addContext(p, bbsContext) 843 844 signatureSuite = bbsblssignature2020.New(suite.WithSigner(s)) 845 default: 846 return fmt.Errorf("unsupported signature type '%s'", opts.ProofType) 847 } 848 849 signingCtx := &verifiable.LinkedDataProofContext{ 850 VerificationMethod: opts.VerificationMethod, 851 SignatureRepresentation: *opts.ProofRepresentation, 852 SignatureType: opts.ProofType, 853 Suite: signatureSuite, 854 Created: opts.Created, 855 Domain: opts.Domain, 856 Challenge: opts.Challenge, 857 Purpose: supportedRelationships[relationship], 858 } 859 860 err = p.AddLinkedDataProof(signingCtx, jsonld.WithDocumentLoader(c.jsonldDocumentLoader)) 861 if err != nil { 862 return fmt.Errorf("failed to add linked data proof: %w", err) 863 } 864 865 return nil 866 } 867 868 func (c *Wallet) validateProofOption(authToken string, opts *ProofOptions, method did.VerificationRelationship) error { 869 if opts == nil || opts.Controller == "" { 870 return errors.New("invalid proof option, 'controller' is required") 871 } 872 873 resolvedDoc, err := newContentBasedVDR(authToken, c.vdr, c.contents).Resolve(opts.Controller) 874 if err != nil { 875 return err 876 } 877 878 err = c.validateVerificationMethod(resolvedDoc.DIDDocument, opts, method) 879 if err != nil { 880 return err 881 } 882 883 if opts.ProofFormat == "" { 884 opts.ProofFormat = EmbeddedLDProofFormat 885 } 886 887 if opts.ProofRepresentation == nil { 888 opts.ProofRepresentation = &defaultSignatureRepresentation 889 } 890 891 if opts.ProofType == "" { 892 opts.ProofType = Ed25519Signature2018 893 } 894 895 return nil 896 } 897 898 func (c *Wallet) validateVerificationMethod(didDoc *did.Doc, opts *ProofOptions, 899 relationship did.VerificationRelationship) error { 900 vms := didDoc.VerificationMethods(relationship)[relationship] 901 902 for _, vm := range vms { 903 if opts.VerificationMethod == "" { 904 opts.VerificationMethod = vm.VerificationMethod.ID 905 return nil 906 } 907 908 if opts.VerificationMethod == vm.VerificationMethod.ID { 909 return nil 910 } 911 } 912 913 return fmt.Errorf("unable to find '%s' for given verification method", supportedRelationships[relationship]) 914 } 915 916 // addContext adds context if not found in given data model. 917 func addContext(v interface{}, ldcontext string) { 918 if vc, ok := v.(*verifiable.Credential); ok { 919 for _, ctx := range vc.Context { 920 if ctx == ldcontext { 921 return 922 } 923 } 924 925 vc.Context = append(vc.Context, ldcontext) 926 } 927 } 928 929 func updateProfile(keyManager kms.KeyManager, profile *profile) error { 930 // setup key pairs 931 err := profile.setupEDVEncryptionKey(keyManager) 932 if err != nil { 933 return fmt.Errorf("failed to create EDV encryption key pair: %w", err) 934 } 935 936 err = profile.setupEDVMacKey(keyManager) 937 if err != nil { 938 return fmt.Errorf("failed to create EDV MAC key pair: %w", err) 939 } 940 941 return nil 942 }