diff --git a/pkg/doc/sdjwt/common/common.go b/pkg/doc/sdjwt/common/common.go index b87b93e38..6622366c0 100644 --- a/pkg/doc/sdjwt/common/common.go +++ b/pkg/doc/sdjwt/common/common.go @@ -272,8 +272,8 @@ func GetSDAlg(claims map[string]interface{}) (string, error) { obj, ok := claims[SDAlgorithmKey] if !ok { - // if claims contain 'vc' claim it may be present in credential subject - obj, ok = GetKeyFromCredentialSubject(SDAlgorithmKey, claims) + // if claims contain 'vc' claim it may be present in vc + obj, ok = GetKeyFromVC(SDAlgorithmKey, claims) if !ok { return "", fmt.Errorf("%s must be present in SD-JWT", SDAlgorithmKey) } @@ -287,28 +287,8 @@ func GetSDAlg(claims map[string]interface{}) (string, error) { return alg, nil } -// GetKeyFromCredentialSubject returns key value from VC credential subject. -func GetKeyFromCredentialSubject(key string, claims map[string]interface{}) (interface{}, bool) { - csObj, ok := GetCredentialSubject(claims) - if !ok { - return nil, false - } - - cs, ok := csObj.(map[string]interface{}) - if !ok { - return nil, false - } - - obj, ok := cs[key] - if !ok { - return nil, false - } - - return obj, true -} - -// GetCredentialSubject returns credential subject from vc. -func GetCredentialSubject(claims map[string]interface{}) (interface{}, bool) { +// GetKeyFromVC returns key value from VC. +func GetKeyFromVC(key string, claims map[string]interface{}) (interface{}, bool) { vcObj, ok := claims["vc"] if !ok { return nil, false @@ -319,19 +299,19 @@ func GetCredentialSubject(claims map[string]interface{}) (interface{}, bool) { return nil, false } - csObj, ok := vc["credentialSubject"] + obj, ok := vc[key] if !ok { return nil, false } - return csObj, true + return obj, true } // GetCNF returns confirmation claim 'cnf'. func GetCNF(claims map[string]interface{}) (map[string]interface{}, error) { obj, ok := claims[CNFKey] if !ok { - obj, ok = GetKeyFromCredentialSubject(CNFKey, claims) + obj, ok = GetKeyFromVC(CNFKey, claims) if !ok { return nil, fmt.Errorf("%s must be present in SD-JWT", CNFKey) } diff --git a/pkg/doc/sdjwt/common/common_test.go b/pkg/doc/sdjwt/common/common_test.go index 1f058e9f2..2dcac8cb0 100644 --- a/pkg/doc/sdjwt/common/common_test.go +++ b/pkg/doc/sdjwt/common/common_test.go @@ -463,9 +463,7 @@ func TestGetSDAlg(t *testing.T) { claims := map[string]interface{}{ "given_name": "John", "vc": map[string]interface{}{ - "credentialSubject": map[string]interface{}{ - "_sd_alg": "sha-256", - }, + "_sd_alg": "sha-256", }, } @@ -474,7 +472,7 @@ func TestGetSDAlg(t *testing.T) { r.Equal("sha-256", alg) }) - t.Run("error - algorithm not found (empty claims)", func(t *testing.T) { + t.Run("error - algorithm not found (no vc)", func(t *testing.T) { alg, err := GetSDAlg(make(map[string]interface{})) r.Error(err) r.Empty(alg) @@ -482,13 +480,9 @@ func TestGetSDAlg(t *testing.T) { r.Contains(err.Error(), "_sd_alg must be present in SD-JWT") }) - t.Run("error - algorithm not found", func(t *testing.T) { + t.Run("error - algorithm not found (vc is empty)", func(t *testing.T) { claims := map[string]interface{}{ - "vc": map[string]interface{}{ - "credentialSubject": map[string]interface{}{ - "given_name": "John", - }, - }, + "vc": map[string]interface{}{}, } alg, err := GetSDAlg(claims) @@ -498,7 +492,7 @@ func TestGetSDAlg(t *testing.T) { r.Contains(err.Error(), "_sd_alg must be present in SD-JWT") }) - t.Run("error - algorithm not found (vc claim is not a map)", func(t *testing.T) { + t.Run("error - algorithm not found (vc is not a map)", func(t *testing.T) { claims := map[string]interface{}{ "vc": "invalid", } @@ -510,40 +504,10 @@ func TestGetSDAlg(t *testing.T) { r.Contains(err.Error(), "_sd_alg must be present in SD-JWT") }) - t.Run("error - algorithm not found (no credential subject in vc)", func(t *testing.T) { - claims := map[string]interface{}{ - "vc": map[string]interface{}{ - "id": "test-id", - }, - } - - alg, err := GetSDAlg(claims) - r.Error(err) - r.Empty(alg) - - r.Contains(err.Error(), "_sd_alg must be present in SD-JWT") - }) - - t.Run("error - algorithm not found (credential subject is not a map)", func(t *testing.T) { - claims := map[string]interface{}{ - "vc": map[string]interface{}{ - "credentialSubject": "invalid", - }, - } - - alg, err := GetSDAlg(claims) - r.Error(err) - r.Empty(alg) - - r.Contains(err.Error(), "_sd_alg must be present in SD-JWT") - }) - t.Run("error - algorithm must be a string", func(t *testing.T) { claims := map[string]interface{}{ "vc": map[string]interface{}{ - "credentialSubject": map[string]interface{}{ - "_sd_alg": 123, - }, + "_sd_alg": 123, }, } @@ -585,7 +549,7 @@ func TestGetCNF(t *testing.T) { r.NotEmpty(cnf["jwk"]) }) - t.Run("success - cnf is in VC credential subject", func(t *testing.T) { + t.Run("success - cnf is in VC", func(t *testing.T) { var payload map[string]interface{} err := json.Unmarshal([]byte(vcSample), &payload) @@ -662,16 +626,16 @@ const vcSample = ` "goPn0hokFnQBktqzXxgTK-4CCldmLjlRwUVCIltDyRg", "FAiNODIxDMwGTljNYcVKkx7LBsr1pb-U6XuAfVFuOGY" ], - "_sd_alg": "sha-256", - "cnf": { - "jwk": { - "crv": "Ed25519", - "kty": "OKP", - "x": "7jtkxxk0Pb3E0O6JXJiN8HyIp2DpCiqaHCWfMXl9ZFo" - } - }, "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" }, + "_sd_alg": "sha-256", + "cnf": { + "jwk": { + "crv": "Ed25519", + "kty": "OKP", + "x": "7jtkxxk0Pb3E0O6JXJiN8HyIp2DpCiqaHCWfMXl9ZFo" + } + }, "first_name": "First name", "id": "http://example.edu/credentials/1872", "info": "Info", diff --git a/pkg/doc/sdjwt/holder/holder_test.go b/pkg/doc/sdjwt/holder/holder_test.go index ac01a1110..c5ba38e36 100644 --- a/pkg/doc/sdjwt/holder/holder_test.go +++ b/pkg/doc/sdjwt/holder/holder_test.go @@ -74,7 +74,7 @@ func TestParse(t *testing.T) { claims, err := Parse(vcCombinedFormatForIssuance, WithSignatureVerifier(&NoopSignatureVerifier{})) r.NoError(err) require.NotNil(t, claims) - require.Equal(t, 3, len(claims)) + require.Equal(t, 4, len(claims)) }) t.Run("success - complex claims", func(t *testing.T) { @@ -290,4 +290,5 @@ const additionalDisclosure = `WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbW // nolint: lll const specSDJWT = `eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZUkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIk5ZQ29TUktFWXdYZHBlNXlkdUpYQ3h4aHluRVU4ei1iNFR5TmlhcDc3VVkiLCAiU1k4bjJCYmtYOWxyWTNleEhsU3dQUkZYb0QwOUdGOGE5Q1BPLUc4ajIwOCIsICJUUHNHTlBZQTQ2d21CeGZ2MnpuT0poZmRvTjVZMUdrZXpicGFHWkNUMWFjIiwgIlprU0p4eGVHbHVJZFlCYjdDcWtaYkpWbTB3MlY1VXJSZU5UekFRQ1lCanciLCAibDlxSUo5SlRRd0xHN09MRUlDVEZCVnhtQXJ3OFBqeTY1ZEQ2bXRRVkc1YyIsICJvMVNBc0ozM1lNaW9POXBYNVZlQU0xbHh1SEY2aFpXMmtHZGtLS0JuVmxvIiwgInFxdmNxbmN6QU1nWXg3RXlrSTZ3d3RzcHl2eXZLNzkwZ2U3TUJiUS1OdXMiXSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIlJTQSIsICJuIjogInBtNGJPSEJnLW9ZaEF5UFd6UjU2QVdYM3JVSVhwMTFfSUNEa0dnUzZXM1pXTHRzLWh6d0kzeDY1NjU5a2c0aFZvOWRiR29DSkUzWkdGX2VhZXRFMzBVaEJVRWdwR3dyRHJRaUo5enFwcm1jRmZyM3F2dmtHanR0aDhaZ2wxZU0yYkpjT3dFN1BDQkhXVEtXWXMxNTJSN2c2SmcyT1ZwaC1hOHJxLXE3OU1oS0c1UW9XX21UejEwUVRfNkg0YzdQaldHMWZqaDhocFdObmJQX3B2NmQxelN3WmZjNWZsNnlWUkwwRFYwVjNsR0hLZTJXcWZfZU5HakJyQkxWa2xEVGs4LXN0WF9NV0xjUi1FR21YQU92MFVCV2l0U19kWEpLSnUtdlhKeXcxNG5IU0d1eFRJSzJoeDFwdHRNZnQ5Q3N2cWltWEtlRFRVMTRxUUwxZUU3aWhjdyIsICJlIjogIkFRQUIifX19.xqgKrDO6dK_oBL3fiqdcq_elaIGxM6Z-RyuysglGyddR1O1IiE3mIk8kCpoqcRLR88opkVWN2392K_XYfAuAmeT9kJVisD8ZcgNcv-MQlWW9s8WaViXxBRe7EZWkWRQcQVR6jf95XZ5H2-_KA54POq3L42xjk0y5vDr8yc08Reak6vvJVvjXpp-Wk6uxsdEEAKFspt_EYIvISFJhfTuQqyhCjnaW13X312MSQBPwjbHn74ylUqVLljDvqcemxeqjh42KWJq4C3RqNJ7anA2i3FU1kB4-KNZWsijY7-op49iL7BrnIBxdlAMrbHEkoGTbFWdl7Ki17GHtDxxa1jaxQg~WyJkcVR2WE14UzBHYTNEb2FHbmU5eDBRIiwgInN1YiIsICJqb2huX2RvZV80MiJd~WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJxUVdtakpsMXMxUjRscWhFTkxScnJ3IiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyJLVXhTNWhFX1hiVmFjckdBYzdFRnd3IiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyIzcXZWSjFCQURwSERTUzkzOVEtUml3IiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyIweEd6bjNNaXFzY3RaSV9PcERsQWJRIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJFUktNMENOZUZKa2FENW1UWFZfWDh3IiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0` -const vcCombinedFormatForIssuance = `eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp0cnVzdGJsb2M6YWJjI2tleTEifQ.eyJpYXQiOjE2NzM5ODc1NDcsImlzcyI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwibmJmIjoxNjczOTg3NTQ3LCJzdWIiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJfc2QiOlsiVklMLXlXZHlZX3hNbk5icEJvbHRrcnlBOFZESVFVYllLd2dQaHVEUEx1ZyIsIkxDLUg0R2N4UG1OdGN5VWNiSGFDbTlEUDFnZDROYXJsZ2RiUFc4ZEVvZ2siLCJlbHVRRFVtbHpwM19naU9uRFVRVk1WLWpWM1hMNXVIck1BNzRROXI0dVF3Il0sIl9zZF9hbGciOiJzaGEtMjU2IiwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifSwiZmlyc3RfbmFtZSI6IkZpcnN0IG5hbWUiLCJpZCI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwiaW5mbyI6IkluZm8iLCJpc3N1YW5jZURhdGUiOiIyMDIzLTAxLTE3VDIyOjMyOjI3LjQ2ODEwOTgxNyswMjowMCIsImlzc3VlciI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImxhc3RfbmFtZSI6Ikxhc3QgbmFtZSIsInR5cGUiOiJWZXJpZmlhYmxlQ3JlZGVudGlhbCJ9fQ.PvRYW8-EAG7K4QQL3TV-GNF--vaYIGc3TWJrRSoc2qBCVT5sFkez7FTLv7iae24S2mi2GH5lcxy1dx75LSjOBA~WyJIb01DbEdxLUpRUUZIMUVZZnFCN1FBIiwic3BvdXNlIiwiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIl0~WyI5ZDFzTUFYUEVTZkEzaTE0NDNzVTRRIiwiZGVncmVlIix7ImRlZ3JlZSI6Ik1JVCIsInR5cGUiOiJCYWNoZWxvckRlZ3JlZSJ9XQ~WyJiekpGY1pYMkYyRjE3XzVsSFU2MjF3IiwibmFtZSIsIkpheWRlbiBEb2UiXQ` // nolint: lll +// nolint: lll +const vcCombinedFormatForIssuance = `eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjEuNjczOTg3NTQ3ZSswOSwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEuNjczOTg3NTQ3ZSswOSwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZDlYemtRbVJMQncxSXpfeHVGUmVLMUItRmpCdTdjT0N3RTlOR2F1d251SSJ9fSwiY3JlZGVudGlhbFN1YmplY3QiOnsiX3NkIjpbInBBdjJUMU10YmRXNGttUUdxT1VVRUpjQmdTZi1mSFRHV2xQVUV4aWlIbVEiLCI2dDlBRUJCQnEzalZwckJ3bGljOGhFWnNNSmxXSXhRdUw5c3ExMzJZTnYwIl0sImRlZ3JlZSI6eyJfc2QiOlsibzZzV2h4RjcxWHBvZ1cxVUxCbU90bjR1SXFGdjJ3ODF6emRuelJXdlpqYyIsIi1yRklXbU1YR3ZXX0FIYVEtODhpMy11ZzRUVjhLUTg5TjdmZmtneFc2X2MiXX0sImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0sImZpcnN0X25hbWUiOiJGaXJzdCBuYW1lIiwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMTg3MiIsImluZm8iOiJJbmZvIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMS0xN1QyMjozMjoyNy40NjgxMDk4MTcrMDI6MDAiLCJpc3N1ZXIiOiJkaWQ6ZXhhbXBsZTo3NmUxMmVjNzEyZWJjNmYxYzIyMWViZmViMWYiLCJsYXN0X25hbWUiOiJMYXN0IG5hbWUiLCJ0eXBlIjoiVmVyaWZpYWJsZUNyZWRlbnRpYWwifX0.GcfSA6NkONxdsm5Lxj9-988eWx1ZvMz5vJ1uh2x8UK1iKIeQLmhsWpA_34RbtAm2HnuoxW4_ZGeiHBzQ1GLTDQ~WyJFWkVDRVZ1YWVJOXhZWmlWb3VMQldBIiwidHlwZSIsIkJhY2hlbG9yRGVncmVlIl0~WyJyMno1UzZMa25FRTR3TWwteFB0VEx3IiwiZGVncmVlIiwiTUlUIl0~WyJ2VkhfaGhNQy1aSUt5WFdtdDUyOWpnIiwic3BvdXNlIiwiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIl0~WyJrVzh0WVVwbVl1VmRoZktFT050TnFnIiwibmFtZSIsIkpheWRlbiBEb2UiXQ` diff --git a/pkg/doc/sdjwt/integration_test.go b/pkg/doc/sdjwt/integration_test.go index 2a79647d7..ceda7a599 100644 --- a/pkg/doc/sdjwt/integration_test.go +++ b/pkg/doc/sdjwt/integration_test.go @@ -258,6 +258,7 @@ func TestSDJWTFlow(t *testing.T) { printObject(t, "Verified Claims", verifiedClaims) }) + // TODO: This test will be deleted; you should use NewFromVC api for creating VC t.Run("success - create VC plus holder binding", func(t *testing.T) { holderPublicKey, holderPrivateKey, err := ed25519.GenerateKey(rand.Reader) r.NoError(err) @@ -297,8 +298,22 @@ func TestSDJWTFlow(t *testing.T) { err = json.Unmarshal([]byte(sampleVC), &vc) r.NoError(err) + const credentialSubjectKey = "credentialSubject" + const vcKey = "vc" + + // move _sd_alg key from credential subject to vc as per example 4 in spec + vc[vcKey].(map[string]interface{})[common.SDAlgorithmKey] = selectiveCredentialSubject[common.SDAlgorithmKey] + delete(selectiveCredentialSubject, common.SDAlgorithmKey) + + // move cnf key from credential subject to vc as per example 4 in spec + cnfObj, ok := selectiveCredentialSubject[common.CNFKey] + if ok { + vc[vcKey].(map[string]interface{})[common.CNFKey] = cnfObj + delete(selectiveCredentialSubject, common.CNFKey) + } + // update VC with 'selective' credential subject - vc["vc"].(map[string]interface{})["credentialSubject"] = selectiveCredentialSubject + vc[vcKey].(map[string]interface{})[credentialSubjectKey] = selectiveCredentialSubject // sign VC with 'selective' credential subject signedJWT, err := afjwt.NewSigned(vc, nil, signer) @@ -371,6 +386,13 @@ func TestSDJWTFlow(t *testing.T) { issuer.WithStructuredClaims(true)) r.NoError(err) + var decoded map[string]interface{} + + err = token.DecodeClaims(&decoded) + require.NoError(t, err) + + printObject(t, "SD-JWT Payload", decoded) + vcCombinedFormatForIssuance, err := token.Serialize(false) r.NoError(err) diff --git a/pkg/doc/sdjwt/issuer/issuer.go b/pkg/doc/sdjwt/issuer/issuer.go index 4b357d9e3..0e4805807 100644 --- a/pkg/doc/sdjwt/issuer/issuer.go +++ b/pkg/doc/sdjwt/issuer/issuer.go @@ -32,6 +32,9 @@ const ( decoyMinElements = 1 decoyMaxElements = 4 + + credentialSubjectKey = "credentialSubject" + vcKey = "vc" ) var mr = mathrand.New(mathrand.NewSource(time.Now().Unix())) // nolint:gochecknoglobals @@ -186,7 +189,7 @@ func New(issuer string, claims interface{}, headers jose.Headers, // NewFromVC creates new signed Selective Disclosure JWT based on vc. func NewFromVC(vc map[string]interface{}, headers jose.Headers, signer jose.Signer, opts ...NewOpt) (*SelectiveDisclosureJWT, error) { - csObj, ok := common.GetCredentialSubject(vc) + csObj, ok := common.GetKeyFromVC(credentialSubjectKey, vc) if !ok { return nil, fmt.Errorf("credential subject not found") } @@ -208,8 +211,19 @@ func NewFromVC(vc map[string]interface{}, headers jose.Headers, return nil, err } + // move _sd_alg key from credential subject to vc as per example 4 in spec + vc[vcKey].(map[string]interface{})[common.SDAlgorithmKey] = selectiveCredentialSubject[common.SDAlgorithmKey] + delete(selectiveCredentialSubject, common.SDAlgorithmKey) + + // move cnf key from credential subject to vc as per example 4 in spec + cnfObj, ok := selectiveCredentialSubject[common.CNFKey] + if ok { + vc[vcKey].(map[string]interface{})[common.CNFKey] = cnfObj + delete(selectiveCredentialSubject, common.CNFKey) + } + // update VC with 'selective' credential subject - vc["vc"].(map[string]interface{})["credentialSubject"] = selectiveCredentialSubject + vc[vcKey].(map[string]interface{})[credentialSubjectKey] = selectiveCredentialSubject // sign VC with 'selective' credential subject signedJWT, err := afgjwt.NewSigned(vc, nil, signer) diff --git a/pkg/doc/sdjwt/verifier/verifier_test.go b/pkg/doc/sdjwt/verifier/verifier_test.go index de8781bbb..89a2cdd47 100644 --- a/pkg/doc/sdjwt/verifier/verifier_test.go +++ b/pkg/doc/sdjwt/verifier/verifier_test.go @@ -832,5 +832,5 @@ func prettyPrint(msg []byte) (string, error) { const additionalDisclosure = `WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd` -const vcCombinedFormatForIssuance = `eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp0cnVzdGJsb2M6YWJjI2tleTEifQ.eyJpYXQiOjE2NzM5ODc1NDcsImlzcyI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwibmJmIjoxNjczOTg3NTQ3LCJzdWIiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJfc2QiOlsiVklMLXlXZHlZX3hNbk5icEJvbHRrcnlBOFZESVFVYllLd2dQaHVEUEx1ZyIsIkxDLUg0R2N4UG1OdGN5VWNiSGFDbTlEUDFnZDROYXJsZ2RiUFc4ZEVvZ2siLCJlbHVRRFVtbHpwM19naU9uRFVRVk1WLWpWM1hMNXVIck1BNzRROXI0dVF3Il0sIl9zZF9hbGciOiJzaGEtMjU2IiwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifSwiZmlyc3RfbmFtZSI6IkZpcnN0IG5hbWUiLCJpZCI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwiaW5mbyI6IkluZm8iLCJpc3N1YW5jZURhdGUiOiIyMDIzLTAxLTE3VDIyOjMyOjI3LjQ2ODEwOTgxNyswMjowMCIsImlzc3VlciI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImxhc3RfbmFtZSI6Ikxhc3QgbmFtZSIsInR5cGUiOiJWZXJpZmlhYmxlQ3JlZGVudGlhbCJ9fQ.PvRYW8-EAG7K4QQL3TV-GNF--vaYIGc3TWJrRSoc2qBCVT5sFkez7FTLv7iae24S2mi2GH5lcxy1dx75LSjOBA~WyJIb01DbEdxLUpRUUZIMUVZZnFCN1FBIiwic3BvdXNlIiwiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIl0~WyI5ZDFzTUFYUEVTZkEzaTE0NDNzVTRRIiwiZGVncmVlIix7ImRlZ3JlZSI6Ik1JVCIsInR5cGUiOiJCYWNoZWxvckRlZ3JlZSJ9XQ~WyJiekpGY1pYMkYyRjE3XzVsSFU2MjF3IiwibmFtZSIsIkpheWRlbiBEb2UiXQ` // nolint: lll -const vcSDJWT = `eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp0cnVzdGJsb2M6YWJjI2tleTEifQ.eyJpYXQiOjE2NzM5ODc1NDcsImlzcyI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImp0aSI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwibmJmIjoxNjczOTg3NTQ3LCJzdWIiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJfc2QiOlsiVklMLXlXZHlZX3hNbk5icEJvbHRrcnlBOFZESVFVYllLd2dQaHVEUEx1ZyIsIkxDLUg0R2N4UG1OdGN5VWNiSGFDbTlEUDFnZDROYXJsZ2RiUFc4ZEVvZ2siLCJlbHVRRFVtbHpwM19naU9uRFVRVk1WLWpWM1hMNXVIck1BNzRROXI0dVF3Il0sIl9zZF9hbGciOiJzaGEtMjU2IiwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEifSwiZmlyc3RfbmFtZSI6IkZpcnN0IG5hbWUiLCJpZCI6Imh0dHA6Ly9leGFtcGxlLmVkdS9jcmVkZW50aWFscy8xODcyIiwiaW5mbyI6IkluZm8iLCJpc3N1YW5jZURhdGUiOiIyMDIzLTAxLTE3VDIyOjMyOjI3LjQ2ODEwOTgxNyswMjowMCIsImlzc3VlciI6ImRpZDpleGFtcGxlOjc2ZTEyZWM3MTJlYmM2ZjFjMjIxZWJmZWIxZiIsImxhc3RfbmFtZSI6Ikxhc3QgbmFtZSIsInR5cGUiOiJWZXJpZmlhYmxlQ3JlZGVudGlhbCJ9fQ.PvRYW8-EAG7K4QQL3TV-GNF--vaYIGc3TWJrRSoc2qBCVT5sFkez7FTLv7iae24S2mi2GH5lcxy1dx75LSjOBA` // nolint: lll +const vcCombinedFormatForIssuance = `eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjEuNjczOTg3NTQ3ZSswOSwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEuNjczOTg3NTQ3ZSswOSwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZDlYemtRbVJMQncxSXpfeHVGUmVLMUItRmpCdTdjT0N3RTlOR2F1d251SSJ9fSwiY3JlZGVudGlhbFN1YmplY3QiOnsiX3NkIjpbInBBdjJUMU10YmRXNGttUUdxT1VVRUpjQmdTZi1mSFRHV2xQVUV4aWlIbVEiLCI2dDlBRUJCQnEzalZwckJ3bGljOGhFWnNNSmxXSXhRdUw5c3ExMzJZTnYwIl0sImRlZ3JlZSI6eyJfc2QiOlsibzZzV2h4RjcxWHBvZ1cxVUxCbU90bjR1SXFGdjJ3ODF6emRuelJXdlpqYyIsIi1yRklXbU1YR3ZXX0FIYVEtODhpMy11ZzRUVjhLUTg5TjdmZmtneFc2X2MiXX0sImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0sImZpcnN0X25hbWUiOiJGaXJzdCBuYW1lIiwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMTg3MiIsImluZm8iOiJJbmZvIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMS0xN1QyMjozMjoyNy40NjgxMDk4MTcrMDI6MDAiLCJpc3N1ZXIiOiJkaWQ6ZXhhbXBsZTo3NmUxMmVjNzEyZWJjNmYxYzIyMWViZmViMWYiLCJsYXN0X25hbWUiOiJMYXN0IG5hbWUiLCJ0eXBlIjoiVmVyaWZpYWJsZUNyZWRlbnRpYWwifX0.GcfSA6NkONxdsm5Lxj9-988eWx1ZvMz5vJ1uh2x8UK1iKIeQLmhsWpA_34RbtAm2HnuoxW4_ZGeiHBzQ1GLTDQ~WyJFWkVDRVZ1YWVJOXhZWmlWb3VMQldBIiwidHlwZSIsIkJhY2hlbG9yRGVncmVlIl0~WyJyMno1UzZMa25FRTR3TWwteFB0VEx3IiwiZGVncmVlIiwiTUlUIl0~WyJ2VkhfaGhNQy1aSUt5WFdtdDUyOWpnIiwic3BvdXNlIiwiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIl0~WyJrVzh0WVVwbVl1VmRoZktFT050TnFnIiwibmFtZSIsIkpheWRlbiBEb2UiXQ` // nolint: lll +const vcSDJWT = `eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjEuNjczOTg3NTQ3ZSswOSwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEuNjczOTg3NTQ3ZSswOSwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZDlYemtRbVJMQncxSXpfeHVGUmVLMUItRmpCdTdjT0N3RTlOR2F1d251SSJ9fSwiY3JlZGVudGlhbFN1YmplY3QiOnsiX3NkIjpbInBBdjJUMU10YmRXNGttUUdxT1VVRUpjQmdTZi1mSFRHV2xQVUV4aWlIbVEiLCI2dDlBRUJCQnEzalZwckJ3bGljOGhFWnNNSmxXSXhRdUw5c3ExMzJZTnYwIl0sImRlZ3JlZSI6eyJfc2QiOlsibzZzV2h4RjcxWHBvZ1cxVUxCbU90bjR1SXFGdjJ3ODF6emRuelJXdlpqYyIsIi1yRklXbU1YR3ZXX0FIYVEtODhpMy11ZzRUVjhLUTg5TjdmZmtneFc2X2MiXX0sImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0sImZpcnN0X25hbWUiOiJGaXJzdCBuYW1lIiwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMTg3MiIsImluZm8iOiJJbmZvIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMS0xN1QyMjozMjoyNy40NjgxMDk4MTcrMDI6MDAiLCJpc3N1ZXIiOiJkaWQ6ZXhhbXBsZTo3NmUxMmVjNzEyZWJjNmYxYzIyMWViZmViMWYiLCJsYXN0X25hbWUiOiJMYXN0IG5hbWUiLCJ0eXBlIjoiVmVyaWZpYWJsZUNyZWRlbnRpYWwifX0.GcfSA6NkONxdsm5Lxj9-988eWx1ZvMz5vJ1uh2x8UK1iKIeQLmhsWpA_34RbtAm2HnuoxW4_ZGeiHBzQ1GLTDQ` // nolint:lll