From c16ea406b3a18df602c219bf0035703f99496431 Mon Sep 17 00:00:00 2001 From: Sandra Vrtikapa Date: Mon, 19 Sep 2022 10:06:50 -0400 Subject: [PATCH] feat: Validate did configuration for did and domain (#3372) Parse and validate DID configuration bytes. Search through domain linkage credential(s) for specified did and domain. Validate domain linkage credential for Linked Data (JWT validation will be handled in different issue). Closes #3371 Signed-off-by: Sandra Vrtikapa Signed-off-by: Sandra Vrtikapa --- pkg/doc/didconfig/didconfig.go | 317 ++++++++++++++++ pkg/doc/didconfig/didconfig_test.go | 550 ++++++++++++++++++++++++++++ 2 files changed, 867 insertions(+) create mode 100644 pkg/doc/didconfig/didconfig.go create mode 100644 pkg/doc/didconfig/didconfig_test.go diff --git a/pkg/doc/didconfig/didconfig.go b/pkg/doc/didconfig/didconfig.go new file mode 100644 index 000000000..2c7785224 --- /dev/null +++ b/pkg/doc/didconfig/didconfig.go @@ -0,0 +1,317 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package didconfig + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + + jsonld "github.com/piprate/json-gold/ld" + + "github.com/hyperledger/aries-framework-go/pkg/common/log" + diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" + "github.com/hyperledger/aries-framework-go/pkg/vdr" + "github.com/hyperledger/aries-framework-go/pkg/vdr/key" +) + +var logger = log.New("aries-framework/doc/verifiable") + +const ( + // ContextV1 of the DID document is the current V1 context name. + ContextV1 = "https://identity.foundation/.well-known/did-configuration/v1" + + domainLinkageCredentialType = "DomainLinkageCredential" + + contextProperty = "@context" + linkedDIDsProperty = "linked_dids" +) + +// didConfigOpts holds options for the DID Configuration decoding. +type didConfigOpts struct { + jsonldDocumentLoader jsonld.DocumentLoader + vdrRegistry vdrapi.Registry +} + +// DIDConfigurationOpt is the DID Configuration decoding option. +type DIDConfigurationOpt func(opts *didConfigOpts) + +// WithJSONLDDocumentLoader defines a JSON-LD document loader. +func WithJSONLDDocumentLoader(documentLoader jsonld.DocumentLoader) DIDConfigurationOpt { + return func(opts *didConfigOpts) { + opts.jsonldDocumentLoader = documentLoader + } +} + +// WithVDRegistry defines a vdr service. +func WithVDRegistry(vdrRegistry vdrapi.Registry) DIDConfigurationOpt { + return func(opts *didConfigOpts) { + opts.vdrRegistry = vdrRegistry + } +} + +type rawDoc struct { + Context string `json:"@context,omitempty"` + LinkedDIDs []interface{} `json:"linked_dids,omitempty"` +} + +// VerifyDIDAndDomain will verify that there is valid domain linkage credential in did configuration +// for specified did and domain. +func VerifyDIDAndDomain(didConfig []byte, did, domain string, opts ...DIDConfigurationOpt) error { + // apply options + didCfgOpts := getDIDConfigurationOpts(opts) + + // verify required and allowed properties in did configuration + err := verifyDidConfigurationProperties(didConfig) + if err != nil { + return err + } + + raw := rawDoc{} + + err = json.Unmarshal(didConfig, &raw) + if err != nil { + return fmt.Errorf("JSON unmarshalling of DID configuration bytes failed: %w", err) + } + + credentials, err := getCredentials(raw.LinkedDIDs, did, domain, didCfgOpts) + if err != nil { + return err + } + + logger.Debugf("found %d domain linkage credential(s) for DID[%s] and domain[%s]", len(credentials), did, domain) + + for _, credBytes := range credentials { + var credOpts []verifiable.CredentialOpt + + credOpts = append(credOpts, + verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(didCfgOpts.vdrRegistry).PublicKeyFetcher()), + verifiable.WithNoCustomSchemaCheck(), + verifiable.WithJSONLDDocumentLoader(didCfgOpts.jsonldDocumentLoader), + verifiable.WithStrictValidation()) + + // this time we are parsing credential with proof check so DID will be resolved + // and public key from did will be used to verify proof + _, err := verifiable.ParseCredential(credBytes, credOpts...) + if err == nil { + // we found domain linkage credential with valid proof so all good + return nil + } + + // failed to verify credential proof - log info and continue to next one + logger.Debugf("skipping domain linkage credential for DID[%s] and domain[%s] due to error: %s", + did, domain, err.Error()) + } + + return fmt.Errorf("domain linkage credential(s) with valid proof not found") +} + +func getDIDConfigurationOpts(opts []DIDConfigurationOpt) *didConfigOpts { + didCfgOpts := &didConfigOpts{ + jsonldDocumentLoader: jsonld.NewDefaultDocumentLoader(http.DefaultClient), + vdrRegistry: vdr.New(vdr.WithVDR(key.New())), + } + + for _, opt := range opts { + opt(didCfgOpts) + } + + return didCfgOpts +} + +func verifyDidConfigurationProperties(data []byte) error { + requiredKeys := []string{contextProperty, linkedDIDsProperty} + allowedKeys := []string{contextProperty, linkedDIDsProperty} + + var didCfgMap map[string]interface{} + + err := json.Unmarshal(data, &didCfgMap) + if err != nil { + return fmt.Errorf("JSON unmarshalling of DID configuration bytes failed: %w", err) + } else if didCfgMap == nil { + return errors.New("DID configuration payload is not provided") + } + + for _, required := range requiredKeys { + if _, ok := didCfgMap[required]; !ok { + return fmt.Errorf("key '%s' is required", required) + } + } + + for key := range didCfgMap { + if !contains(key, allowedKeys) { + return fmt.Errorf("key '%s' is not allowed", key) + } + } + + return nil +} + +func isValidDomainLinkageCredential(vc *verifiable.Credential, did, origin string) error { + if !contains(domainLinkageCredentialType, vc.Types) { + return fmt.Errorf("credential is not of %s type", domainLinkageCredentialType) + } + + if vc.ID != "" { + return fmt.Errorf("id MUST NOT be present") + } + + if vc.Issued == nil { + return fmt.Errorf("issuance date MUST be present") + } + + if vc.Expired == nil { + return fmt.Errorf("expiration date MUST be present") + } + + if vc.Subject == nil { + return fmt.Errorf("subject MUST be present") + } + + return validateSubject(vc.Subject, did, origin) +} + +func contains(v string, values []string) bool { + for _, val := range values { + if v == val { + return true + } + } + + return false +} + +func validateSubject(subject interface{}, did, origin string) error { + switch s := subject.(type) { + case []verifiable.Subject: + if len(s) > 1 { + // TODO: Can we have more than one subject in this case + return fmt.Errorf("encountered multiple subjects") + } + + subject := s[0] + + if subject.ID == "" { + return fmt.Errorf("credentialSubject.id MUST be present") + } + + _, err := diddoc.Parse(subject.ID) + if err != nil { + return fmt.Errorf("credentialSubject.id MUST be a DID: %w", err) + } + + if subject.ID != did { + return fmt.Errorf("credential subject ID[%s] is different from requested did[%s]", subject.ID, did) + } + + objOrigin, ok := subject.CustomFields["origin"] + if !ok { + return fmt.Errorf("credentialSubject.origin MUST be present") + } + + sOrigin, ok := objOrigin.(string) + if !ok { + return fmt.Errorf("credentialSubject.origin MUST be string") + } + + err = validateOrigin(sOrigin, origin) + if err != nil { + return err + } + + default: + return fmt.Errorf("unexpected interface[%T] for subject", subject) + } + + return nil +} + +func validateOrigin(origin1, origin2 string) error { + url1, err := url.Parse(origin1) + if err != nil { + return err + } + + url2, err := url.Parse(origin2) + if err != nil { + return err + } + + // Browsers define same origin based on the following pieces of data: + // The protocol (e.g., HTTP or HTTPS) + // The port, if available + // The host + if url1.Host != url2.Host || url1.Scheme != url2.Scheme || url1.Port() != url2.Port() { + return fmt.Errorf("origin[%s] and domain origin[%s] are different", origin1, origin2) + } + + return nil +} + +func getCredentials(linkedDIDs []interface{}, did, domain string, opts *didConfigOpts) ([][]byte, error) { + var credentialsForDIDAndDomain [][]byte + + for _, linkedDID := range linkedDIDs { + var rawBytes []byte + + var err error + + switch linkedDID := linkedDID.(type) { + case string: // JWT + rawBytes = []byte(linkedDID) + case map[string]interface{}: // Linked Data + rawBytes, err = json.Marshal(linkedDID) + if err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unexpected interface[%T] for linked DID", linkedDID) + } + + var credOpts []verifiable.CredentialOpt + + credOpts = append(credOpts, + verifiable.WithDisabledProofCheck(), + verifiable.WithNoCustomSchemaCheck(), + verifiable.WithJSONLDDocumentLoader(opts.jsonldDocumentLoader), + verifiable.WithStrictValidation()) + + vc, err := verifiable.ParseCredential(rawBytes, credOpts...) + if err != nil { + // failed to parse credential - continue to next one + logger.Debugf("skipping credential due to error: %s", string(rawBytes), err.Error()) + + continue + } + + if vc.Issuer.ID != did { + logger.Debugf("skipping credential since issuer[%s] is different from DID[%s]", vc.Issuer.ID, did) + + continue + } + + err = isValidDomainLinkageCredential(vc, did, domain) + if err != nil { + logger.Warnf("credential is not a valid domain linkage credential for DID[%s] and domain[%s]: %s", + did, domain, err.Error()) + + continue + } + + credentialsForDIDAndDomain = append(credentialsForDIDAndDomain, rawBytes) + } + + if len(credentialsForDIDAndDomain) == 0 { + return nil, fmt.Errorf("domain linkage credential(s) not found") + } + + return credentialsForDIDAndDomain, nil +} diff --git a/pkg/doc/didconfig/didconfig_test.go b/pkg/doc/didconfig/didconfig_test.go new file mode 100644 index 000000000..3322223c9 --- /dev/null +++ b/pkg/doc/didconfig/didconfig_test.go @@ -0,0 +1,550 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package didconfig + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hyperledger/aries-framework-go/pkg/doc/ldcontext" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" + "github.com/hyperledger/aries-framework-go/pkg/vdr" + "github.com/hyperledger/aries-framework-go/pkg/vdr/key" +) + +const ( + testDID = "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + testDomain = "https://identity.foundation" +) + +func TestParseOfNull(t *testing.T) { + err := VerifyDIDAndDomain([]byte("null"), testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "DID configuration payload is not provided") +} + +func TestParseLinkedData(t *testing.T) { + loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ + URL: ContextV1, + Content: json.RawMessage(didConfigCtx), + }) + require.NoError(t, err) + + t.Run("success - default options", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), testDID, testDomain) + require.NoError(t, err) + }) + + t.Run("success - loader provided", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), testDID, testDomain, + WithJSONLDDocumentLoader(loader)) + require.NoError(t, err) + }) + + t.Run("success - registry provided", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.NoError(t, err) + }) + + t.Run("error - invalid proof", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataInvalidProof), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), "domain linkage credential(s) with valid proof not found") + }) + + t.Run("error - origins do not match", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), testDID, "https://different.com", + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + + require.Contains(t, err.Error(), "domain linkage credential(s) not found") + }) + + t.Run("error - DIDs do not match", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), "did:web:different", testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + + require.Contains(t, err.Error(), "domain linkage credential(s) not found") + }) + + t.Run("error - origin invalid", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedData), testDID, "://different.com", + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), "domain linkage credential(s) not found") + }) + + t.Run("error - unmarshal error", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte("invalid-json"), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), + "JSON unmarshalling of DID configuration bytes failed: invalid character") + }) + + t.Run("error - extra property", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataExtraProperty), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), "key 'extra' is not allowed") + }) + + t.Run("error - did configuration missing context", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataNoContext), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), "key '@context' is required") + }) + + t.Run("error - did configuration missing linked DIDs", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataNoLinkedDIDs), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), "key 'linked_dids' is required") + }) + + t.Run("error - unexpected interface for linked DIDs", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataInvalidLinkedDIDs), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), + "unexpected interface[float64] for linked DID") + }) + + t.Run("error - invalid VC", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgLinkedDataInvalidVC), testDID, testDomain, + WithJSONLDDocumentLoader(loader), + WithVDRegistry(vdr.New(vdr.WithVDR(key.New())))) + require.Error(t, err) + require.Contains(t, err.Error(), + "domain linkage credential(s) not found") + }) +} + +func TestParseValidJWT(t *testing.T) { + loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ + URL: "https://identity.foundation/.well-known/did-configuration/v1", + Content: json.RawMessage(didConfigCtx), + }) + require.NoError(t, err) + + t.Run("success - default options", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgJWT), + testDID, "identity.foundation") + require.NoError(t, err) + }) + + t.Run("success - options provided", func(t *testing.T) { + err := VerifyDIDAndDomain([]byte(didCfgJWT), + testDID, "identity.foundation", + WithJSONLDDocumentLoader(loader)) + require.NoError(t, err) + }) +} + +func TestIsValidDomainLinkageCredential(t *testing.T) { + loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ + URL: ContextV1, + Content: json.RawMessage(didConfigCtx), + }) + require.NoError(t, err) + + var credOpts []verifiable.CredentialOpt + + credOpts = append(credOpts, + verifiable.WithDisabledProofCheck(), + verifiable.WithNoCustomSchemaCheck(), + verifiable.WithJSONLDDocumentLoader(loader), + verifiable.WithStrictValidation()) + + t.Run("success", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.NoError(t, err) + }) + + t.Run("error - different DID", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + err = isValidDomainLinkageCredential(vc, "did:method:id", testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), + "credential subject ID[did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM] is different from requested did[did:method:id]") //nolint:lll + }) + + t.Run("error - different domain", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + err = isValidDomainLinkageCredential(vc, testDID, "https://different.com") + require.Error(t, err) + require.Contains(t, err.Error(), + "origin[https://identity.foundation] and domain origin[https://different.com] are different") + }) + + t.Run("error - credential is not of DomainLinkageCredential type", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Types = nil + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "credential is not of DomainLinkageCredential type") + }) + + t.Run("error - credential has ID", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.ID = "https://domain.com/vc-id" + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "id MUST NOT be present") + }) + + t.Run("error - no issuance date", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Issued = nil + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "issuance date MUST be present") + }) + + t.Run("error - no expiration date", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Expired = nil + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "expiration date MUST be present") + }) + + t.Run("error - no subject", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject = nil + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "subject MUST be present") + }) + + t.Run("error - no subject origin ", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + delete(vc.Subject.([]verifiable.Subject)[0].CustomFields, "origin") + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "credentialSubject.origin MUST be present") + }) + + t.Run("error - subject origin must be a string", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject.([]verifiable.Subject)[0].CustomFields["origin"] = nil + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "credentialSubject.origin MUST be string") + }) + + t.Run("error - multiple subjects", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject = append(vc.Subject.([]verifiable.Subject), vc.Subject.([]verifiable.Subject)[0]) + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "encountered multiple subjects") + }) + + t.Run("error - unexpected interface for subject", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject = make(map[string]string) + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "unexpected interface[map[string]string] for subject") + }) + + t.Run("error - no subject ID", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject.([]verifiable.Subject)[0].ID = "" + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "credentialSubject.id MUST be present") + }) + + t.Run("error - subject ID is not DID", func(t *testing.T) { + vc, err := verifiable.ParseCredential([]byte(dlc), credOpts...) + require.NoError(t, err) + + vc.Subject.([]verifiable.Subject)[0].ID = "not-did" + + err = isValidDomainLinkageCredential(vc, testDID, testDomain) + require.Error(t, err) + require.Contains(t, err.Error(), "credentialSubject.id MUST be a DID") + }) +} + +// nolint: lll,gochecknoglobals +var didCfgLinkedData = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "issuanceDate": "2020-12-04T14:08:28-06:00", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM#z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + } + } + ] +}` + +// nolint: lll,gochecknoglobals +var didCfgLinkedDataInvalidProof = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "issuanceDate": "2020-12-04T14:08:28-06:00", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod" + } + } + ] +}` + +// nolint: lll,gochecknoglobals +var didCfgLinkedDataInvalidVC = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM#z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + } + } + ] +}` + +// nolint: lll,gochecknoglobals +var didCfgLinkedDataExtraProperty = ` +{ + "extra": "value", + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "issuanceDate": "2020-12-04T14:08:28-06:00", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM#z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + } + } + ] +}` + +// nolint: gochecknoglobals +var didCfgLinkedDataNoLinkedDIDs = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1" +}` + +// nolint: lll,gochecknoglobals +var didCfgLinkedDataNoContext = ` +{ + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "issuanceDate": "2020-12-04T14:08:28-06:00", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM#z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + } + } + ] +}` + +// nolint: gochecknoglobals +var didCfgLinkedDataInvalidLinkedDIDs = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ 1, 2 ] +}` + +// nolint: lll,gochecknoglobals +var dlc = ` +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "issuer": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "issuanceDate": "2020-12-04T14:08:28-06:00", + "expirationDate": "2025-12-04T14:08:28-06:00", + "type": [ + "VerifiableCredential", + "DomainLinkageCredential" + ], + "credentialSubject": { + "id": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM", + "origin": "https://identity.foundation" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2020-12-04T20:08:28.540Z", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..D0eDhglCMEjxDV9f_SNxsuU-r3ZB9GR4vaM9TYbyV7yzs1WfdUyYO8rFZdedHbwQafYy8YOpJ1iJlkSmB4JaDQ", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM#z6MkoTHsgNNrby8JzCNQ1iRLyW5QQ6R8Xuu6AA8igGrMVPUM" + } +}` + +// nolint: lll,gochecknoglobals +var didCfgJWT = ` +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + "eyJhbGciOiJFZERTQSJ9.eyJleHAiOjE3NjQ4Nzg5MDgsImlzcyI6ImRpZDprZXk6ejZNa29USHNnTk5yYnk4SnpDTlExaVJMeVc1UVE2UjhYdXU2QUE4aWdHck1WUFVNIiwibmJmIjoxNjA3MTEyNTA4LCJzdWIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly9pZGVudGl0eS5mb3VuZGF0aW9uLy53ZWxsLWtub3duL2RpZC1jb25maWd1cmF0aW9uL3YxIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6Nk1rb1RIc2dOTnJieThKekNOUTFpUkx5VzVRUTZSOFh1dTZBQThpZ0dyTVZQVU0iLCJvcmlnaW4iOiJpZGVudGl0eS5mb3VuZGF0aW9uIn0sImV4cGlyYXRpb25EYXRlIjoiMjAyNS0xMi0wNFQxNDowODoyOC0wNjowMCIsImlzc3VhbmNlRGF0ZSI6IjIwMjAtMTItMDRUMTQ6MDg6MjgtMDY6MDAiLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWtvVEhzZ05OcmJ5OEp6Q05RMWlSTHlXNVFRNlI4WHV1NkFBOGlnR3JNVlBVTSIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJEb21haW5MaW5rYWdlQ3JlZGVudGlhbCJdfX0.6ovgQ-T_rmYueviySqXhzMzgqJMAizOGUKAObQr2iikoRNsb8DHfna4rh1puwWqYwgT3QJVpzdO_xZARAYM9Dw" + ] +}` + +// nolint: lll,gochecknoglobals +var didConfigCtx = ` +{ + "@context": [ + { + "@version": 1.1, + "@protected": true, + "LinkedDomains": "https://identity.foundation/.well-known/resources/did-configuration/#LinkedDomains", + "DomainLinkageCredential": "https://identity.foundation/.well-known/resources/did-configuration/#DomainLinkageCredential", + "origin": "https://identity.foundation/.well-known/resources/did-configuration/#origin", + "linked_dids": "https://identity.foundation/.well-known/resources/did-configuration/#linked_dids" + } + ] +}`