github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/jsonld/validate_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package jsonld 7 8 import ( 9 _ "embed" 10 "fmt" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 15 "github.com/hyperledger/aries-framework-go/pkg/doc/ld" 16 "github.com/hyperledger/aries-framework-go/pkg/doc/ldcontext" 17 "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" 18 ) 19 20 //nolint:gochecknoglobals 21 var ( 22 //go:embed testdata/context/context3.jsonld 23 context3 []byte 24 //go:embed testdata/context/context4.jsonld 25 context4 []byte 26 //go:embed testdata/context/context5.jsonld 27 context5 []byte 28 //go:embed testdata/context/context6.jsonld 29 context6 []byte 30 31 //go:embed testdata/context/wallet_v1.jsonld 32 walletV1Context []byte 33 ) 34 35 func Test_ValidateJSONLD(t *testing.T) { 36 t.Run("Extended both basic VC and subject model", func(t *testing.T) { 37 contextURL := "http://127.0.0.1?context=3" 38 39 vcJSONTemplate := `{ 40 "@context": [ 41 "https://www.w3.org/2018/credentials/v1", 42 "%s" 43 ], 44 "id": "http://example.com/credentials/4643", 45 "type": [ 46 "VerifiableCredential", 47 "CustomExt12" 48 ], 49 "issuer": "https://example.com/issuers/14", 50 "issuanceDate": "2018-02-24T05:28:04Z", 51 "referenceNumber": 83294847, 52 "credentialSubject": [ 53 { 54 "id": "did:example:abcdef1234567", 55 "name": "Jane Doe", 56 "favoriteFood": "Papaya" 57 }, 58 { 59 "id": "did:example:abcdef1234568", 60 "name": "Alex" 61 }, 62 { 63 "id": "did:example:abcdef1234569", 64 "name": "Justin" 65 } 66 ], 67 "proof": [ 68 { 69 "type": "Ed25519Signature2018", 70 "created": "2020-04-10T21:35:35Z", 71 "verificationMethod": "did:key:z6MkjRag", 72 "proofPurpose": "assertionMethod", 73 "jws": "eyJ..l9d0Y" 74 }, 75 { 76 "type": "Ed25519Signature2018", 77 "created": "2020-04-11T21:35:35Z", 78 "verificationMethod": "did:key:z6MkjRll", 79 "proofPurpose": "assertionMethod", 80 "jws": "eyJ..l9dZq" 81 } 82 ], 83 "termsOfUse": [ 84 { 85 "type": [ 86 "IssuerPolicy", 87 "HolderPolicy" 88 ], 89 "id": "http://example.com/policies/credential/4" 90 }, 91 { 92 "type": [ 93 "IssuerPolicy", 94 "HolderPolicy" 95 ], 96 "id": "http://example.com/policies/credential/5" 97 } 98 ] 99 } 100 ` 101 vc := fmt.Sprintf(vcJSONTemplate, contextURL) 102 103 loader := createTestDocumentLoader(t, ldcontext.Document{ 104 URL: "http://127.0.0.1?context=3", 105 Content: context3, 106 }) 107 108 err := ValidateJSONLD(vc, WithDocumentLoader(loader)) 109 require.NoError(t, err) 110 }) 111 112 t.Run("Extended basic VC model, credentialSubject is defined as string (ID only)", func(t *testing.T) { 113 // Use a different VC to verify the case when credentialSubject is a string (i.e. ID is defined only). 114 115 contextURL := "http://127.0.0.1?context=4" 116 117 vcJSONTemplate := ` 118 { 119 "@context": [ 120 "https://www.w3.org/2018/credentials/v1", 121 "%s" 122 ], 123 "id": "http://example.com/credentials/4643", 124 "type": [ 125 "VerifiableCredential", 126 "CustomExt12" 127 ], 128 "issuer": "https://example.com/issuers/14", 129 "issuanceDate": "2018-02-24T05:28:04Z", 130 "referenceNumber": 83294847, 131 "credentialSubject": "did:example:abcdef1234567" 132 } 133 ` 134 vcJSON := fmt.Sprintf(vcJSONTemplate, contextURL) 135 136 loader := createTestDocumentLoader(t, ldcontext.Document{ 137 URL: "http://127.0.0.1?context=4", 138 Content: context4, 139 }) 140 141 err := ValidateJSONLD(vcJSON, WithDocumentLoader(loader)) 142 require.NoError(t, err) 143 }) 144 } 145 146 func Test_ValidateJSONLDWithExtraUndefinedFields(t *testing.T) { 147 contextURL := "http://127.0.0.1?context=5" 148 149 vcJSONTemplate := ` 150 { 151 "@context": [ 152 "https://www.w3.org/2018/credentials/v1", 153 "%s" 154 ], 155 "id": "http://example.com/credentials/4643", 156 "type": ["VerifiableCredential", "CustomExt12"], 157 "issuer": "https://example.com/issuers/14", 158 "issuanceDate": "2018-02-24T05:28:04Z", 159 "referenceNumber": 83294847, 160 "credentialSubject": { 161 "id": "did:example:abcdef1234567", 162 "name": "Jane Doe", 163 "favoriteFood": "Papaya" 164 } 165 } 166 ` 167 vc := fmt.Sprintf(vcJSONTemplate, contextURL) 168 169 loader := createTestDocumentLoader(t, ldcontext.Document{ 170 URL: "http://127.0.0.1?context=5", 171 Content: context5, 172 }) 173 174 err := ValidateJSONLD(vc, WithDocumentLoader(loader)) 175 require.Error(t, err) 176 require.EqualError(t, err, "JSON-LD doc has different structure after compaction") 177 } 178 179 func Test_ValidateJSONLDWithExtraUndefinedSubjectFields(t *testing.T) { 180 contextURL := "http://127.0.0.1?context=6" 181 182 loader := createTestDocumentLoader(t, ldcontext.Document{ 183 URL: contextURL, 184 Content: context6, 185 }) 186 187 t.Run("Extended basic VC model, credentialSubject is defined as object - undefined fields present", 188 func(t *testing.T) { 189 // Use a different VC to verify the case when credentialSubject is an array. 190 vcJSONTemplate := ` 191 { 192 "@context": [ 193 "https://www.w3.org/2018/credentials/v1", 194 "%s" 195 ], 196 "id": "http://example.com/credentials/4643", 197 "type": [ 198 "VerifiableCredential", 199 "CustomExt12" 200 ], 201 "issuer": "https://example.com/issuers/14", 202 "issuanceDate": "2018-02-24T05:28:04Z", 203 "referenceNumber": 83294847, 204 "credentialSubject": [ 205 { 206 "id": "did:example:abcdef1234567", 207 "name": "Jane Doe", 208 "favoriteFood": "Papaya" 209 } 210 ] 211 } 212 ` 213 214 vcJSON := fmt.Sprintf(vcJSONTemplate, contextURL) 215 216 err := ValidateJSONLD(vcJSON, WithDocumentLoader(loader)) 217 require.Error(t, err) 218 require.EqualError(t, err, "JSON-LD doc has different structure after compaction") 219 }) 220 221 t.Run("Extended basic VC model, credentialSubject is defined as array - undefined fields present", func(t *testing.T) { 222 // Use a different VC to verify the case when credentialSubject is an array. 223 vcJSONTemplate := ` 224 { 225 "@context": [ 226 "https://www.w3.org/2018/credentials/v1", 227 "%s" 228 ], 229 "id": "http://example.com/credentials/4643", 230 "type": [ 231 "VerifiableCredential", 232 "CustomExt12" 233 ], 234 "issuer": "https://example.com/issuers/14", 235 "issuanceDate": "2018-02-24T05:28:04Z", 236 "referenceNumber": 83294847, 237 "credentialSubject": [ 238 { 239 "id": "did:example:abcdef1234567", 240 "name": "Jane Doe", 241 "favoriteFood": "Papaya" 242 } 243 ] 244 } 245 ` 246 247 vcJSON := fmt.Sprintf(vcJSONTemplate, contextURL) 248 249 err := ValidateJSONLD(vcJSON, WithDocumentLoader(loader)) 250 require.Error(t, err) 251 require.EqualError(t, err, "JSON-LD doc has different structure after compaction") 252 }) 253 } 254 255 func Test_ValidateJSONLD_WithExtraUndefinedFieldsInProof(t *testing.T) { 256 vcJSONWithValidProof := ` 257 { 258 "@context": [ 259 "https://www.w3.org/2018/credentials/v1" 260 ], 261 "id": "http://example.com/credentials/4643", 262 "type": [ 263 "VerifiableCredential" 264 ], 265 "issuer": "https://example.com/issuers/14", 266 "issuanceDate": "2018-02-24T05:28:04Z", 267 "credentialSubject": [ 268 { 269 "id": "did:example:abcdef1234567" 270 } 271 ], 272 "proof": { 273 "type": "Ed25519Signature2018", 274 "created": "2020-04-10T21:35:35Z", 275 "verificationMethod": "did:key:z6MkjRag", 276 "proofPurpose": "assertionMethod", 277 "jws": "eyJ..l9d0Y" 278 } 279 } 280 ` 281 282 err := ValidateJSONLD(vcJSONWithValidProof, WithDocumentLoader(createTestDocumentLoader(t))) 283 require.NoError(t, err) 284 285 // "newProp" field is present in the proof 286 vcJSONWithInvalidProof := `{ 287 "@context": [ 288 "https://www.w3.org/2018/credentials/v1" 289 ], 290 "id": "http://example.com/credentials/4643", 291 "type": [ 292 "VerifiableCredential" 293 ], 294 "issuer": "https://example.com/issuers/14", 295 "issuanceDate": "2018-02-24T05:28:04Z", 296 "credentialSubject": [ 297 { 298 "id": "did:example:abcdef1234567" 299 } 300 ], 301 "proof": { 302 "type": "Ed25519Signature2018", 303 "created": "2020-04-10T21:35:35Z", 304 "verificationMethod": "did:key:z6MkjRag", 305 "proofPurpose": "assertionMethod", 306 "jws": "eyJ..l9d0Y", 307 "newProp": "foo" 308 } 309 }` 310 311 err = ValidateJSONLD(vcJSONWithInvalidProof, WithDocumentLoader(createTestDocumentLoader(t))) 312 313 require.Error(t, err) 314 require.EqualError(t, err, "JSON-LD doc has different structure after compaction") 315 } 316 317 func Test_ValidateJSONLD_CornerErrorCases(t *testing.T) { 318 t.Run("Invalid JSON input", func(t *testing.T) { 319 err := ValidateJSONLD("not a json", WithDocumentLoader(createTestDocumentLoader(t))) 320 require.Error(t, err) 321 require.Contains(t, err.Error(), "convert JSON-LD doc to map") 322 }) 323 324 t.Run("JSON-LD compact error", func(t *testing.T) { 325 vcJSONTemplate := ` 326 { 327 "@context": 777, 328 "id": "http://example.com/credentials/4643", 329 "type": [ 330 "VerifiableCredential", 331 "CustomExt12" 332 ], 333 "issuer": "https://example.com/issuers/14", 334 "issuanceDate": "2018-02-24T05:28:04Z", 335 "referenceNumber": 83294847, 336 "credentialSubject": "did:example:abcdef1234567" 337 } 338 ` 339 340 err := ValidateJSONLD(vcJSONTemplate, WithDocumentLoader(createTestDocumentLoader(t))) 341 require.Error(t, err) 342 require.Contains(t, err.Error(), "compact JSON-LD document") 343 }) 344 345 t.Run("JSON-LD WithStrictContextURIPosition invalid context", func(t *testing.T) { 346 vcJSONTemplate := ` 347 { 348 "@context": "https://www.w3.org/2018/credentials/v1", 349 "id": "http://example.com/credentials/4643", 350 "type": [ 351 "VerifiableCredential" 352 ], 353 "issuer": "https://example.com/issuers/14", 354 "issuanceDate": "2018-02-24T05:28:04Z", 355 "credentialSubject": "did:example:abcdef1234567" 356 } 357 ` 358 359 err := ValidateJSONLD(vcJSONTemplate, 360 WithDocumentLoader(createTestDocumentLoader(t)), 361 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/v1"), 362 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/examples/v1")) 363 require.Error(t, err) 364 require.Contains(t, err.Error(), "doc context URIs amount mismatch") 365 }) 366 367 t.Run("JSON-LD WithStrictContextURIPosition invalid context URI amount", func(t *testing.T) { 368 vcJSONTemplate := ` 369 { 370 "@context": [ 371 "https://www.w3.org/2018/credentials/v1" 372 ], 373 "id": "http://example.com/credentials/4643", 374 "type": [ 375 "VerifiableCredential" 376 ], 377 "issuer": "https://example.com/issuers/14", 378 "issuanceDate": "2018-02-24T05:28:04Z", 379 "credentialSubject": "did:example:abcdef1234567" 380 } 381 ` 382 383 err := ValidateJSONLD(vcJSONTemplate, 384 WithDocumentLoader(createTestDocumentLoader(t)), 385 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/v1"), 386 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/examples/v1"), 387 ) 388 require.Error(t, err) 389 require.Contains(t, err.Error(), "doc context URIs amount mismatch") 390 }) 391 392 t.Run("JSON-LD WithStrictContextURIPosition validate context URI position", func(t *testing.T) { 393 vcJSONTemplate := ` 394 { 395 "@context": [ 396 "https://www.w3.org/2018/credentials/v1", 397 "https://www.w3.org/2018/credentials/examples/v1" 398 ], 399 "id": "http://example.com/credentials/4643", 400 "type": [ 401 "VerifiableCredential" 402 ], 403 "issuer": "https://example.com/issuers/14", 404 "issuanceDate": "2018-02-24T05:28:04Z", 405 "credentialSubject": "did:example:abcdef1234567" 406 } 407 ` 408 409 err := ValidateJSONLD(vcJSONTemplate, 410 WithDocumentLoader(createTestDocumentLoader(t)), 411 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/examples/v1"), 412 ) 413 require.Error(t, err) 414 require.Contains(t, err.Error(), "invalid context URI on position") 415 416 err = ValidateJSONLD(vcJSONTemplate, 417 WithDocumentLoader(createTestDocumentLoader(t)), 418 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/v1"), 419 WithStrictContextURIPosition("https://www.w3.org/2018/credentials/examples/v1"), 420 ) 421 require.NoError(t, err) 422 }) 423 } 424 425 // nolint:gochecknoglobals // needed to avoid Go compiler perf optimizations for benchmarks. 426 var MajorSink string 427 428 func Benchmark_ValidateJSONLD(b *testing.B) { 429 var sink string 430 431 b.Run("Extended both basic VC and subject model", func(b *testing.B) { 432 contextURL := "http://127.0.0.1?context=3" 433 434 vcJSONTemplate := `{ 435 "@context": [ 436 "https://www.w3.org/2018/credentials/v1", 437 "%s" 438 ], 439 "id": "http://example.com/credentials/4643", 440 "type": [ 441 "VerifiableCredential", 442 "CustomExt12" 443 ], 444 "issuer": "https://example.com/issuers/14", 445 "issuanceDate": "2018-02-24T05:28:04Z", 446 "referenceNumber": 83294847, 447 "credentialSubject": [ 448 { 449 "id": "did:example:abcdef1234567", 450 "name": "Jane Doe", 451 "favoriteFood": "Papaya" 452 }, 453 { 454 "id": "did:example:abcdef1234568", 455 "name": "Alex" 456 }, 457 { 458 "id": "did:example:abcdef1234569", 459 "name": "Justin" 460 } 461 ], 462 "proof": [ 463 { 464 "type": "Ed25519Signature2018", 465 "created": "2020-04-10T21:35:35Z", 466 "verificationMethod": "did:key:z6MkjRag", 467 "proofPurpose": "assertionMethod", 468 "jws": "eyJ..l9d0Y" 469 }, 470 { 471 "type": "Ed25519Signature2018", 472 "created": "2020-04-11T21:35:35Z", 473 "verificationMethod": "did:key:z6MkjRll", 474 "proofPurpose": "assertionMethod", 475 "jws": "eyJ..l9dZq" 476 } 477 ], 478 "termsOfUse": [ 479 { 480 "type": [ 481 "IssuerPolicy", 482 "HolderPolicy" 483 ], 484 "id": "http://example.com/policies/credential/4" 485 }, 486 { 487 "type": [ 488 "IssuerPolicy", 489 "HolderPolicy" 490 ], 491 "id": "http://example.com/policies/credential/5" 492 } 493 ] 494 } 495 ` 496 497 vc := fmt.Sprintf(vcJSONTemplate, contextURL) 498 499 b.RunParallel(func(pb *testing.PB) { 500 b.ResetTimer() 501 502 for pb.Next() { 503 loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ 504 URL: "http://127.0.0.1?context=3", 505 Content: context3, 506 }) 507 require.NoError(b, err) 508 509 err = ValidateJSONLD(vc, WithDocumentLoader(loader)) 510 require.NoError(b, err) 511 512 sink = "basic_compact_test" 513 } 514 515 MajorSink = sink 516 }) 517 }) 518 519 b.Run("Extended basic VC model, credentialSubject is defined as string (ID only)", func(b *testing.B) { 520 // Use a different VC to verify the case when credentialSubject is a string (i.e. ID is defined only). 521 522 contextURL := "http://127.0.0.1?context=4" 523 524 vcJSONTemplate := ` 525 { 526 "@context": [ 527 "https://www.w3.org/2018/credentials/v1", 528 "%s" 529 ], 530 "id": "http://example.com/credentials/4643", 531 "type": [ 532 "VerifiableCredential", 533 "CustomExt12" 534 ], 535 "issuer": "https://example.com/issuers/14", 536 "issuanceDate": "2018-02-24T05:28:04Z", 537 "referenceNumber": 83294847, 538 "credentialSubject": "did:example:abcdef1234567" 539 } 540 ` 541 vcJSON := fmt.Sprintf(vcJSONTemplate, contextURL) 542 543 b.RunParallel(func(pb *testing.PB) { 544 b.ResetTimer() 545 546 for pb.Next() { 547 loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ 548 URL: "http://127.0.0.1?context=4", 549 Content: context4, 550 }) 551 require.NoError(b, err) 552 553 err = ValidateJSONLD(vcJSON, WithDocumentLoader(loader)) 554 require.NoError(b, err) 555 556 sink = "extended_compact_test" 557 } 558 559 MajorSink = sink 560 }) 561 }) 562 563 b.Run("Extended both basic VC and subject model", func(b *testing.B) { 564 const testMetadata = `{ 565 "@context": ["https://w3id.org/wallet/v1"], 566 "id": "test-id", 567 "type": "Person", 568 "name": "John Smith", 569 "image": "https://via.placeholder.com/150", 570 "description" : "Professional software developer for Acme Corp." 571 }` 572 573 b.RunParallel(func(pb *testing.PB) { 574 b.ResetTimer() 575 576 for pb.Next() { 577 loader, err := ldtestutil.DocumentLoader(ldcontext.Document{ 578 URL: "https://w3id.org/wallet/v1", 579 Content: walletV1Context, 580 }) 581 require.NoError(b, err) 582 583 err = ValidateJSONLD(testMetadata, WithDocumentLoader(loader)) 584 require.NoError(b, err) 585 586 sink = "basic_compact_test" 587 } 588 589 MajorSink = sink 590 }) 591 }) 592 } 593 594 func createTestDocumentLoader(t *testing.T, extraContexts ...ldcontext.Document) *ld.DocumentLoader { 595 t.Helper() 596 597 loader, err := ldtestutil.DocumentLoader(extraContexts...) 598 require.NoError(t, err) 599 600 return loader 601 }