diff --git a/component/models/dataintegrity/integration_test.go b/component/models/dataintegrity/integration_test.go index d273b9413..969f1d661 100644 --- a/component/models/dataintegrity/integration_test.go +++ b/component/models/dataintegrity/integration_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/stretchr/testify/require" + "github.com/tidwall/sjson" "github.com/hyperledger/aries-framework-go/component/kmscrypto/crypto/tinkcrypto" "github.com/hyperledger/aries-framework-go/component/kmscrypto/doc/util/jwkkid" @@ -187,6 +188,38 @@ func TestIntegration(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "failed to verify ecdsa-2019 DI proof") }) + t.Run("malformed proof created", func(t *testing.T) { + signOpts := &models.ProofOptions{ + VerificationMethod: p256VM, + VerificationMethodID: p256VM.ID, + SuiteType: ecdsa2019.SuiteType, + Purpose: "assertionMethod", + VerificationRelationship: "assertionMethod", + ProofType: models.DataIntegrityProof, + Created: time.Now(), + } + + verifyOpts := &models.ProofOptions{ + VerificationMethod: p384VM, + VerificationMethodID: p384VM.ID, + SuiteType: ecdsa2019.SuiteType, + Purpose: "assertionMethod", + VerificationRelationship: "assertionMethod", + ProofType: models.DataIntegrityProof, + MaxAge: 100, + Created: time.Time{}, + } + + signedCred, err := signer.AddProof(validCredential, signOpts) + require.NoError(t, err) + + signedCredStr, err := sjson.Set(string(signedCred), "proof.created", "malformed") + require.NoError(t, err) + + err = verifier.VerifyProof([]byte(signedCredStr), verifyOpts) + require.Error(t, err) + require.Contains(t, err.Error(), "malformed data integrity proof") + }) }) } diff --git a/component/models/dataintegrity/suite/ecdsa2019/ecdsa2019.go b/component/models/dataintegrity/suite/ecdsa2019/ecdsa2019.go index 2ffadd6d9..35187d516 100644 --- a/component/models/dataintegrity/suite/ecdsa2019/ecdsa2019.go +++ b/component/models/dataintegrity/suite/ecdsa2019/ecdsa2019.go @@ -164,6 +164,7 @@ func (s *Suite) CreateProof(doc []byte, opts *models.ProofOptions) (*models.Proo Challenge: opts.Challenge, VerificationMethod: opts.VerificationMethod.ID, ProofValue: sigStr, + Created: opts.Created.Format(models.DateTimeFormat), } return p, nil diff --git a/component/models/dataintegrity/verifier.go b/component/models/dataintegrity/verifier.go index 17c366c7c..e43113609 100644 --- a/component/models/dataintegrity/verifier.go +++ b/component/models/dataintegrity/verifier.go @@ -123,6 +123,17 @@ func (v *Verifier) VerifyProof(doc []byte, opts *models.ProofOptions) error { // return ErrMalformedProof } + if opts.Created.IsZero() { + var parsedCreatedTime time.Time + + parsedCreatedTime, err = time.Parse(models.DateTimeFormat, proof.Created) + if err != nil { + return ErrMalformedProof + } + + opts.Created = parsedCreatedTime + } + if proof.ProofPurpose != opts.Purpose { return ErrMismatchedPurpose } diff --git a/component/models/dataintegrity/verifier_test.go b/component/models/dataintegrity/verifier_test.go index 59dca0f0c..08478f5e7 100644 --- a/component/models/dataintegrity/verifier_test.go +++ b/component/models/dataintegrity/verifier_test.go @@ -289,6 +289,7 @@ func TestVerifier_VerifyProof(t *testing.T) { require.NoError(t, err) err = v.VerifyProof(signedDoc, &models.ProofOptions{ + Created: time.Now(), Purpose: "different-purpose", }) require.ErrorIs(t, err, ErrMismatchedPurpose) @@ -316,6 +317,7 @@ func TestVerifier_VerifyProof(t *testing.T) { err = v.VerifyProof(signedDoc, &models.ProofOptions{ Purpose: "mock-purpose", + Created: time.Now(), MaxAge: 1000, }) require.Error(t, err) @@ -348,6 +350,7 @@ func TestVerifier_VerifyProof(t *testing.T) { err = v.VerifyProof(signedDoc, &models.ProofOptions{ Purpose: "mock-purpose", + Created: time.Now(), MaxAge: 1000, }) require.Error(t, err) @@ -386,6 +389,7 @@ func TestVerifier_VerifyProof(t *testing.T) { err = v.VerifyProof(signedDoc, &models.ProofOptions{ Purpose: "mock-purpose", + Created: time.Now(), }) require.ErrorIs(t, err, errExpected) }) @@ -492,6 +496,7 @@ func TestVerifier_VerifyProof(t *testing.T) { err = v.VerifyProof(signedDoc, &models.ProofOptions{ Purpose: "mock-purpose", Domain: "mock-domain", + Created: time.Now(), }) require.ErrorIs(t, err, ErrInvalidDomain) }) @@ -527,6 +532,7 @@ func TestVerifier_VerifyProof(t *testing.T) { err = v.VerifyProof(signedDoc, &models.ProofOptions{ Purpose: "mock-purpose", Challenge: "mock-challenge", + Created: time.Now(), }) require.ErrorIs(t, err, ErrInvalidChallenge) }) diff --git a/component/models/ld/testutil/contexts/third_party/w3id.org/data-integrity-v1.jsonld b/component/models/ld/testutil/contexts/third_party/w3id.org/data-integrity-v1.jsonld new file mode 100644 index 000000000..d8a50543e --- /dev/null +++ b/component/models/ld/testutil/contexts/third_party/w3id.org/data-integrity-v1.jsonld @@ -0,0 +1,74 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "proof": { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph" + }, + "DataIntegrityProof": { + "@id": "https://w3id.org/security#DataIntegrityProof", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "cryptosuite": "https://w3id.org/security#cryptosuite", + "proofValue": { + "@id": "https://w3id.org/security#proofValue", + "@type": "https://w3id.org/security#multibase" + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} \ No newline at end of file diff --git a/component/models/ld/testutil/document_loader.go b/component/models/ld/testutil/document_loader.go index 0731c7794..a6e510f85 100644 --- a/component/models/ld/testutil/document_loader.go +++ b/component/models/ld/testutil/document_loader.go @@ -34,6 +34,8 @@ var ( vcExamples []byte //go:embed contexts/third_party/trustbloc.github.io/trustbloc-authorization-credential_v1.jsonld authCred []byte + //go:embed contexts/third_party/w3id.org/data-integrity-v1.jsonld + dataIntegrity []byte ) var testContexts = []ldcontext.Document{ //nolint:gochecknoglobals // embedded test contexts @@ -63,6 +65,10 @@ var testContexts = []ldcontext.Document{ //nolint:gochecknoglobals // embedded t DocumentURL: "https://raw.githubusercontent.com/w3c-ccg/vc-status-list-2021/343b8b59cddba4525e1ef355356ae760fc75904e/contexts/v1.jsonld", //nolint:lll Content: revocationList2021, }, + { + URL: "https://w3id.org/security/data-integrity/v1", + Content: dataIntegrity, + }, } // WithDocumentLoader returns an option with a custom JSON-LD document loader preloaded with embedded contexts. diff --git a/component/models/verifiable/data_integrity_proof_test.go b/component/models/verifiable/data_integrity_proof_test.go index 2d2b63921..24b4e08e1 100644 --- a/component/models/verifiable/data_integrity_proof_test.go +++ b/component/models/verifiable/data_integrity_proof_test.go @@ -26,7 +26,8 @@ func Test_DataIntegrity_SignVerify(t *testing.T) { { "@context": [ "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" + "https://www.w3.org/2018/credentials/examples/v1", + "https://w3id.org/security/data-integrity/v1" ], "id": "https://example.com/credentials/1872", "type": [ @@ -64,7 +65,7 @@ func Test_DataIntegrity_SignVerify(t *testing.T) { const vmID = "#key-1" - vm, err := did.NewVerificationMethodFromJWK(vmID, "JsonWebKey2020", signingDID, key) + vm, err := did.NewVerificationMethodFromJWK(signingDID+vmID, "JsonWebKey2020", signingDID, key) require.NoError(t, err) resolver := resolveFunc(func(id string) (*did.DocResolution, error) { @@ -103,7 +104,7 @@ func Test_DataIntegrity_SignVerify(t *testing.T) { require.NoError(t, err) t.Run("credential", func(t *testing.T) { - vc, e := parseTestCredential(t, []byte(vcJSON), WithDisabledProofCheck()) + vc, e := parseTestCredential(t, []byte(vcJSON), WithDisabledProofCheck(), WithStrictValidation()) require.NoError(t, e) e = vc.AddDataIntegrityProof(signContext, signer) @@ -112,7 +113,7 @@ func Test_DataIntegrity_SignVerify(t *testing.T) { vcBytes, e := vc.MarshalJSON() require.NoError(t, e) - _, e = parseTestCredential(t, vcBytes, WithDataIntegrityVerifier(verifier)) + _, e = parseTestCredential(t, vcBytes, WithDataIntegrityVerifier(verifier), WithStrictValidation()) require.NoError(t, e) t.Run("fail if not provided verifier", func(t *testing.T) {