From 30d3f21cf76699f98aeb6898cf2080fcb9639000 Mon Sep 17 00:00:00 2001 From: reinkrul Date: Tue, 5 Dec 2023 13:44:07 +0100 Subject: [PATCH] Update to go-did v0.11 (#2634) * Update to go-did v0.10.0 * copyright * add ValidFrom to defaultCredentialValidator * fix e2e test * PR feedback * fix build * revert * f * Revert array compaction * gomodtidy * move validateAtTime to ProofOptions.ValidAt * move validateAtTime to ProofOptions.ValidAt --------- Co-authored-by: Gerard Snaauw --- api/ssi_types_test.go | 3 +- auth/api/auth/v1/api_test.go | 3 +- auth/api/iam/api_test.go | 2 +- auth/services/oauth/relying_party_test.go | 5 ++- auth/services/selfsigned/signer.go | 2 +- didman/didman.go | 5 +++ discovery/test.go | 5 ++- go.mod | 2 +- go.sum | 4 +- vcr/api/vcr/v2/types_test.go | 7 +-- vcr/credential/test.go | 3 +- vcr/credential/validator.go | 3 +- vcr/credential/validator_test.go | 30 +++++++++++++ vcr/issuer/issuer.go | 7 +-- vcr/issuer/issuer_test.go | 7 +-- vcr/issuer/network_publisher.go | 2 +- vcr/issuer/network_publisher_test.go | 12 +++-- vcr/signature/proof/jsonld.go | 14 ++++++ vcr/signature/proof/jsonld_test.go | 36 +++++++++++++++ vcr/test/openid4vci_integration_test.go | 3 +- vcr/verifier/verifier.go | 33 +++++--------- vcr/verifier/verifier_test.go | 53 +---------------------- vdr/didkey/resolver_test.go | 2 +- vdr/didnuts/ambassador_test.go | 2 +- vdr/didnuts/validators.go | 5 ++- vdr/vdr.go | 2 +- vdr/vdr_test.go | 2 +- 27 files changed, 148 insertions(+), 106 deletions(-) diff --git a/api/ssi_types_test.go b/api/ssi_types_test.go index 91155745c4..151d9754df 100644 --- a/api/ssi_types_test.go +++ b/api/ssi_types_test.go @@ -122,6 +122,7 @@ func Test_DIDDocumentMetadata(t *testing.T) { } func createVerifiableCredential() vcr.VerifiableCredential { + issuanceDate := time.Now() return vcr.VerifiableCredential{ Context: []ssi.URI{ssi.MustParseURI("https://www.w3.org/2018/credentials/v1")}, Type: []ssi.URI{ @@ -129,7 +130,7 @@ func createVerifiableCredential() vcr.VerifiableCredential { ssi.MustParseURI("VerifiableCredential"), }, Issuer: ssi.MustParseURI("did:nuts:CuE3qeFGGLhEAS3gKzhMCeqd1dGa9at5JCbmCfyMU2Ey"), - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{"subject"}, Proof: []interface{}{"because"}, } diff --git a/auth/api/auth/v1/api_test.go b/auth/api/auth/v1/api_test.go index 3e7e1b8604..2ea6a3f2ea 100644 --- a/auth/api/auth/v1/api_test.go +++ b/auth/api/auth/v1/api_test.go @@ -489,13 +489,14 @@ func TestWrapper_RequestAccessToken(t *testing.T) { t.Run("happy_path", func(t *testing.T) { ctx := createContext(t) + issuanceDate := time.Now() credentials := []vc.VerifiableCredential{ { Context: []ssi.URI{vc.VCContextV1URI(), credential.NutsV1ContextURI}, ID: &ssi.URI{}, Type: []ssi.URI{*credential.NutsAuthorizationCredentialTypeURI, vc.VerifiableCredentialTypeV1URI()}, Issuer: vdr.TestDIDA.URI(), - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{credential.NutsAuthorizationCredentialSubject{ ID: vdr.TestDIDB.String(), PurposeOfUse: "eTransfer", diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index 3f4f7e728c..7dd1cab95d 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -106,7 +106,7 @@ func TestWrapper_GetWebDID(t *testing.T) { ID: webDID, } // remarshal expectedWebDIDDoc to make sure in-memory format is the same as the one returned by the API - data, _ := expectedWebDIDDoc.MarshalJSON() + data, _ := json.Marshal(expectedWebDIDDoc) _ = expectedWebDIDDoc.UnmarshalJSON(data) t.Run("ok", func(t *testing.T) { diff --git a/auth/services/oauth/relying_party_test.go b/auth/services/oauth/relying_party_test.go index 6c040c2641..7b57d2635e 100644 --- a/auth/services/oauth/relying_party_test.go +++ b/auth/services/oauth/relying_party_test.go @@ -159,7 +159,7 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) { }) t.Run("error - no matching credentials", func(t *testing.T) { ctx := createOAuthRPContext(t) - ctx.wallet.EXPECT().List(gomock.Any(), walletDID).Return([]vcr.VerifiableCredential{}, nil) + ctx.wallet.EXPECT().List(gomock.Any(), walletDID).Return([]vc.VerifiableCredential{}, nil) _, err := ctx.relyingParty.RequestRFC021AccessToken(context.Background(), walletDID, ctx.verifierDID, scopes) @@ -257,12 +257,13 @@ func TestService_CreateJwtBearerToken(t *testing.T) { id := vdr.TestDIDA.URI() id.Fragment = "1" + issuanceDate := time.Now() validCredential := vc.VerifiableCredential{ Context: []ssi.URI{vc.VCContextV1URI(), credential.NutsV1ContextURI}, ID: &id, Type: []ssi.URI{*credential.NutsAuthorizationCredentialTypeURI, vc.VerifiableCredentialTypeV1URI()}, Issuer: vdr.TestDIDA.URI(), - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{credential.NutsAuthorizationCredentialSubject{ ID: vdr.TestDIDB.String(), PurposeOfUse: "eTransfer", diff --git a/auth/services/selfsigned/signer.go b/auth/services/selfsigned/signer.go index c4a74f2d5c..f660a92508 100644 --- a/auth/services/selfsigned/signer.go +++ b/auth/services/selfsigned/signer.go @@ -124,7 +124,7 @@ func (v *signer) createVP(ctx context.Context, s types.Session, issuanceDate tim Context: []ssi.URI{credential.NutsV1ContextURI}, Type: []ssi.URI{ssi.MustParseURI(credentialType)}, Issuer: issuerID.URI(), - IssuanceDate: issuanceDate, + IssuanceDate: &issuanceDate, ExpirationDate: &expirationData, CredentialSubject: s.CredentialSubject(), } diff --git a/didman/didman.go b/didman/didman.go index 0ff4584f0b..c7b17aba2f 100644 --- a/didman/didman.go +++ b/didman/didman.go @@ -599,6 +599,11 @@ func (d *didman) updateService(ctx context.Context, id did.DID, serviceType stri func generateIDForService(id did.DID, service did.Service) ssi.URI { bytes, _ := json.Marshal(service) + // go-did earlier unmarshaled/marshaled the service endpoint to a map[string]interface{} ("NormalizeDocument()"), which changes the order of the keys. + // To retain the same hash given as before go-did v0.10.0, we need to mimic this behavior. + var raw map[string]interface{} + _ = json.Unmarshal(bytes, &raw) + bytes, _ = json.Marshal(raw) shaBytes := sha256.Sum256(bytes) d := id.URI() d.Fragment = base58.EncodeAlphabet(shaBytes[:], base58.BTCAlphabet) diff --git a/discovery/test.go b/discovery/test.go index c8a2444202..ead7807120 100644 --- a/discovery/test.go +++ b/discovery/test.go @@ -129,7 +129,8 @@ func createCredential(issuerDID did.DID, subjectDID did.DID, credentialSubject m vcID := did.DIDURL{DID: issuerDID} vcID.Fragment = uuid.NewString() vcIDURI := vcID.URI() - expirationDate := time.Now().Add(time.Hour * 24) + issuanceDate := time.Now() + expirationDate := issuanceDate.Add(time.Hour * 24) if credentialSubject == nil { credentialSubject = make(map[string]interface{}) } @@ -138,7 +139,7 @@ func createCredential(issuerDID did.DID, subjectDID did.DID, credentialSubject m ID: &vcIDURI, Type: []ssi.URI{ssi.MustParseURI("VerifiableCredential"), ssi.MustParseURI("TestCredential")}, Issuer: issuerDID.URI(), - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, ExpirationDate: &expirationDate, CredentialSubject: []interface{}{credentialSubject}, }, func(ctx context.Context, claims map[string]interface{}, headers map[string]interface{}) (string, error) { diff --git a/go.mod b/go.mod index 0642498d93..f5f01d4e96 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/nats-io/nats-server/v2 v2.10.6 github.com/nats-io/nats.go v1.31.0 github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b - github.com/nuts-foundation/go-did v0.9.0 + github.com/nuts-foundation/go-did v0.11.0 github.com/nuts-foundation/go-leia/v4 v4.0.1 github.com/nuts-foundation/go-stoabs v1.9.0 // check the oapi-codegen tool version in the makefile when upgrading the runtime diff --git a/go.sum b/go.sum index 2342335da0..651d63112b 100644 --- a/go.sum +++ b/go.sum @@ -448,8 +448,8 @@ github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatR github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b h1:80icUxWHwE1MrIOOEK5rxrtyKOgZeq5Iu1IjAEkggTY= github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b/go.mod h1:6YUioYirD6/8IahZkoS4Ypc8xbeJW76Xdk1QKcziNTM= -github.com/nuts-foundation/go-did v0.9.0 h1:JBz1cYaMxplKZ31QyWierrR3Yt2RIpaxZTt8KFm4Ph4= -github.com/nuts-foundation/go-did v0.9.0/go.mod h1:L39mh6SBsuenqeZw2JxARx4a/bwdARwchG2x3zPMTjc= +github.com/nuts-foundation/go-did v0.11.0 h1:RTem1MlVoOOoLa/Y2miYRy70Jex0/kJBTCPH5RtUmrY= +github.com/nuts-foundation/go-did v0.11.0/go.mod h1:2e2H2Hqk0SWrrGZEg97dbK/ZFIkkFB65hNWdOSbylrg= github.com/nuts-foundation/go-leia/v4 v4.0.1 h1:+Sbk3Bew1QnRUqRXSOwomMw3nIZgncmTX425J7U5Q34= github.com/nuts-foundation/go-leia/v4 v4.0.1/go.mod h1:eaZuWIolpU61TMvTMcen85+SOEOnHiALdg5SxqLXzz8= github.com/nuts-foundation/go-stoabs v1.9.0 h1:zK+ugfolaJYyBvGwsRuavLVdycXk4Yw/1gI+tz17lWQ= diff --git a/vcr/api/vcr/v2/types_test.go b/vcr/api/vcr/v2/types_test.go index 63b424908f..5706d590ce 100644 --- a/vcr/api/vcr/v2/types_test.go +++ b/vcr/api/vcr/v2/types_test.go @@ -56,13 +56,14 @@ func Test_Marshalling(t *testing.T) { assert.IsType(t, make(map[string]interface{}, 0), result["credentialSubject"]) // single entry should not end up as slice }) t.Run("CreateVP200JSONResponse", func(t *testing.T) { - r := CreateVP200JSONResponse{ + issuanceDate := time.Now() + r := CreateVP200JSONResponse(VerifiablePresentation{ VerifiableCredential: []VerifiableCredential{ { - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, }, }, - } + }) data, _ := json.Marshal(r) result := make(map[string]interface{}, 0) err := json.Unmarshal(data, &result) diff --git a/vcr/credential/test.go b/vcr/credential/test.go index 41cf48022b..4e1f7d87d5 100644 --- a/vcr/credential/test.go +++ b/vcr/credential/test.go @@ -38,12 +38,13 @@ import ( func ValidNutsAuthorizationCredential() *vc.VerifiableCredential { id := stringToURI(vdr.TestDIDA.String() + "#38E90E8C-F7E5-4333-B63A-F9DD155A0272") + issuanceDate := time.Now() return &vc.VerifiableCredential{ Context: []ssi.URI{vc.VCContextV1URI(), NutsV1ContextURI}, ID: &id, Type: []ssi.URI{*NutsAuthorizationCredentialTypeURI, vc.VerifiableCredentialTypeV1URI()}, Issuer: stringToURI(vdr.TestDIDA.String()), - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{ NutsAuthorizationCredentialSubject{ ID: vdr.TestDIDB.String(), diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index 39803f709b..a87b5991d4 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -101,7 +101,8 @@ func (d defaultCredentialValidator) Validate(credential vc.VerifiableCredential) return failure("'ID' is required") } - if credential.IssuanceDate.IsZero() { + if (credential.IssuanceDate == nil || credential.IssuanceDate.IsZero()) && + (credential.ValidFrom == nil || credential.ValidFrom.IsZero()) { return failure("'issuanceDate' is required") } diff --git a/vcr/credential/validator_test.go b/vcr/credential/validator_test.go index 5b4002b523..4cb71aae65 100644 --- a/vcr/credential/validator_test.go +++ b/vcr/credential/validator_test.go @@ -27,6 +27,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "testing" + "time" ) func init() { @@ -419,6 +420,17 @@ func TestDefaultCredentialValidator(t *testing.T) { assert.NoError(t, err) }) + t.Run("ok - ValidFrom instead of IssuanceDate", func(t *testing.T) { + v := ValidNutsOrganizationCredential(t) + v.IssuanceDate, v.ValidFrom = v.ValidFrom, v.IssuanceDate + + err := validator.Validate(v) + + assert.Nil(t, v.IssuanceDate) + assert.NotEmpty(t, v.ValidFrom) + assert.NoError(t, err) + }) + t.Run("failed - missing ID", func(t *testing.T) { v := ValidNutsOrganizationCredential(t) v.ID = nil @@ -454,4 +466,22 @@ func TestDefaultCredentialValidator(t *testing.T) { assert.EqualError(t, err, "validation failed: type 'VerifiableCredential' is required") }) + + t.Run("failed - issuanceDate and validFrom both missing", func(t *testing.T) { + v := ValidNutsOrganizationCredential(t) + v.IssuanceDate = nil + + err := validator.Validate(v) + + assert.EqualError(t, err, "validation failed: 'issuanceDate' is required") + }) + + t.Run("failed - issuanceDate is zero", func(t *testing.T) { + v := ValidNutsOrganizationCredential(t) + v.IssuanceDate = new(time.Time) + + err := validator.Validate(v) + + assert.EqualError(t, err, "validation failed: 'issuanceDate' is required") + }) } diff --git a/vcr/issuer/issuer.go b/vcr/issuer/issuer.go index 620d5a02c6..ed148e2f40 100644 --- a/vcr/issuer/issuer.go +++ b/vcr/issuer/issuer.go @@ -215,8 +215,9 @@ func (i issuer) buildVC(ctx context.Context, template vc.VerifiableCredential, o ExpirationDate: template.ExpirationDate, IssuanceDate: template.IssuanceDate, } - if unsignedCredential.IssuanceDate.IsZero() { - unsignedCredential.IssuanceDate = TimeFunc() + if unsignedCredential.IssuanceDate == nil { + issuanceDate := TimeFunc() + unsignedCredential.IssuanceDate = &issuanceDate } if !unsignedCredential.ContainsContext(vc.VCContextV1URI()) { unsignedCredential.Context = append(unsignedCredential.Context, vc.VCContextV1URI()) @@ -246,7 +247,7 @@ func (i issuer) buildJSONLDCredential(ctx context.Context, unsignedCredential vc b, _ := json.Marshal(unsignedCredential) _ = json.Unmarshal(b, &credentialAsMap) - proofOptions := proof.ProofOptions{Created: unsignedCredential.IssuanceDate} + proofOptions := proof.ProofOptions{Created: *unsignedCredential.IssuanceDate} webSig := signature.JSONWebSignature2020{ContextLoader: i.jsonldManager.DocumentLoader(), Signer: i.keyStore} signingResult, err := proof.NewLDProof(proofOptions).Sign(ctx, credentialAsMap, webSig, key) diff --git a/vcr/issuer/issuer_test.go b/vcr/issuer/issuer_test.go index 9f3cc9e30e..e70d89c998 100644 --- a/vcr/issuer/issuer_test.go +++ b/vcr/issuer/issuer_test.go @@ -66,7 +66,7 @@ func Test_issuer_buildVC(t *testing.T) { Context: []ssi.URI{schemaOrgContext}, Type: []ssi.URI{credentialType}, Issuer: issuerID, - IssuanceDate: issuance, + IssuanceDate: &issuance, ExpirationDate: &expirationDate, CredentialSubject: []interface{}{map[string]interface{}{ "id": subjectDID, @@ -137,7 +137,7 @@ func Test_issuer_buildVC(t *testing.T) { // Assert JWT require.NotNil(t, result.JWT()) assert.Equal(t, subjectDID, result.JWT().Subject()) - assert.Equal(t, result.IssuanceDate, result.JWT().NotBefore()) + assert.Equal(t, *result.IssuanceDate, result.JWT().NotBefore()) assert.Equal(t, *result.ExpirationDate, result.JWT().Expiration()) assert.Equal(t, result.ID.String(), result.JWT().JwtID()) }) @@ -151,11 +151,12 @@ func Test_issuer_buildVC(t *testing.T) { jsonldManager := jsonld.NewTestJSONLDManager(t) sut := issuer{keyResolver: keyResolverMock, jsonldManager: jsonldManager, keyStore: keyStore} + issuanceDate := time.Now() template := vc.VerifiableCredential{ Context: []ssi.URI{vc.VCContextV1URI()}, Type: []ssi.URI{credentialType}, Issuer: issuerID, - IssuanceDate: time.Now(), + IssuanceDate: &issuanceDate, } result, err := sut.buildVC(ctx, template, CredentialOptions{}) diff --git a/vcr/issuer/network_publisher.go b/vcr/issuer/network_publisher.go index 7eaac674cc..f361e819cb 100644 --- a/vcr/issuer/network_publisher.go +++ b/vcr/issuer/network_publisher.go @@ -86,7 +86,7 @@ func (p networkPublisher) PublishCredential(ctx context.Context, verifiableCrede payload, _ := json.Marshal(verifiableCredential) tx := network.TransactionTemplate(types.VcDocumentType, payload, key). - WithTimestamp(verifiableCredential.IssuanceDate). + WithTimestamp(*verifiableCredential.IssuanceDate). WithAdditionalPrevs(meta.SourceTransactions). WithPrivate(participants) diff --git a/vcr/issuer/network_publisher_test.go b/vcr/issuer/network_publisher_test.go index 29b2b0612a..117fd8023d 100644 --- a/vcr/issuer/network_publisher_test.go +++ b/vcr/issuer/network_publisher_test.go @@ -105,8 +105,10 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { sut := networkPublisher{keyResolver: mockKeyResolver, didResolver: mockDidResolver, networkTx: mockNetwork} + issuanceDate := time.Now() credentialToPublish := vc.VerifiableCredential{ Issuer: issuerID, + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{credential.BaseCredentialSubject{ID: subjectID.String()}}, } payload, _ := json.Marshal(credentialToPublish) @@ -120,7 +122,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { Payload: payload, Type: types.VcDocumentType, AttachKey: false, - Timestamp: time.Time{}, + Timestamp: issuanceDate, AdditionalPrevs: nil, Participants: []did.DID{}, } @@ -145,8 +147,10 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { serviceResolver: mockServiceResolver, } + issuanceDate := time.Now() credentialToPublish := vc.VerifiableCredential{ Issuer: issuerID, + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{credential.BaseCredentialSubject{ID: subjectID.String()}}, } payload, _ := json.Marshal(credentialToPublish) @@ -165,7 +169,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { Payload: payload, Type: types.VcDocumentType, AttachKey: false, - Timestamp: time.Time{}, + Timestamp: issuanceDate, AdditionalPrevs: nil, Participants: []did.DID{*issuerDID, *subjectDID}, } @@ -258,8 +262,10 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { sut := networkPublisher{keyResolver: mockKeyResolver, didResolver: mockDidResolver, networkTx: mockNetwork} + issuanceDate := time.Now() credentialToPublish := vc.VerifiableCredential{ Issuer: issuerID, + IssuanceDate: &issuanceDate, CredentialSubject: []interface{}{credential.BaseCredentialSubject{ID: subjectID.String()}}, } payload, _ := json.Marshal(credentialToPublish) @@ -273,7 +279,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { Payload: payload, Type: types.VcDocumentType, AttachKey: false, - Timestamp: time.Time{}, + Timestamp: issuanceDate, AdditionalPrevs: nil, Participants: make([]did.DID, 0), } diff --git a/vcr/signature/proof/jsonld.go b/vcr/signature/proof/jsonld.go index a86dc2fe44..124ff075fd 100644 --- a/vcr/signature/proof/jsonld.go +++ b/vcr/signature/proof/jsonld.go @@ -64,6 +64,20 @@ type ProofOptions struct { ProofPurpose string `json:"proofPurpose"` } +// ValidAt checks if the proof is valid at a certain given time. +func (o ProofOptions) ValidAt(at time.Time, maxSkew time.Duration) bool { + // check if issuanceDate is before validAt + if o.Created.After(at.Add(maxSkew)) { + return false + } + + // check if expirationDate is after validAt + if o.Expires != nil && o.Expires.Add(maxSkew).Before(at) { + return false + } + return true +} + // LDProof contains the fields of the Proof data model: https://w3c-ccg.github.io/data-integrity-spec/#proofs type LDProof struct { ProofOptions diff --git a/vcr/signature/proof/jsonld_test.go b/vcr/signature/proof/jsonld_test.go index a1a3a07c78..7ae8106e99 100644 --- a/vcr/signature/proof/jsonld_test.go +++ b/vcr/signature/proof/jsonld_test.go @@ -245,3 +245,39 @@ func TestLDProof_Sign(t *testing.T) { assert.Nil(t, result) }) } + +func TestProofOptions_ValidAt(t *testing.T) { + at := time.Now() + skew := 5 * time.Second + t.Run("valid", func(t *testing.T) { + exp := at.Add(1 * time.Hour) + valid := ProofOptions{ + Created: at.Add(-1 * time.Hour), + Expires: &exp, + }.ValidAt(at, skew) + assert.True(t, valid) + }) + + t.Run("not yet valid", func(t *testing.T) { + valid := ProofOptions{ + Created: at.Add(time.Hour), + }.ValidAt(at, skew) + assert.False(t, valid) + }) + + t.Run("expiration not set", func(t *testing.T) { + valid := ProofOptions{ + Created: at.Add(-1 * time.Hour), + }.ValidAt(at, skew) + assert.True(t, valid) + }) + + t.Run("expired", func(t *testing.T) { + exp := at.Add(-1 * time.Hour) + valid := ProofOptions{ + Created: at.Add(-2 * time.Hour), + Expires: &exp, + }.ValidAt(at, skew) + assert.False(t, valid) + }) +} diff --git a/vcr/test/openid4vci_integration_test.go b/vcr/test/openid4vci_integration_test.go index 0aed84a117..e735aa8f5e 100644 --- a/vcr/test/openid4vci_integration_test.go +++ b/vcr/test/openid4vci_integration_test.go @@ -259,6 +259,7 @@ func TestOpenID4VCIErrorResponses(t *testing.T) { } func testCredential() vc.VerifiableCredential { + issuanceDate := time.Now().Truncate(time.Second) return vc.VerifiableCredential{ Context: []ssi.URI{ didnuts.JWS2020ContextV1URI(), @@ -267,7 +268,7 @@ func testCredential() vc.VerifiableCredential { Type: []ssi.URI{ ssi.MustParseURI("NutsAuthorizationCredential"), }, - IssuanceDate: time.Now().Truncate(time.Second), + IssuanceDate: &issuanceDate, } } diff --git a/vcr/verifier/verifier.go b/vcr/verifier/verifier.go index e1b60fce94..e52325e420 100644 --- a/vcr/verifier/verifier.go +++ b/vcr/verifier/verifier.go @@ -89,27 +89,6 @@ func NewVerifier(store Store, didResolver resolver.DIDResolver, keyResolver reso return &verifier{store: store, didResolver: didResolver, keyResolver: keyResolver, jsonldManager: jsonldManager, trustConfig: trustConfig} } -// validateAtTime is a helper method which checks if a credential/presentation is valid at a certain given time. -// If no validAt is provided, validAt is set to now. -func (v *verifier) validateAtTime(issuanceDate time.Time, expirationDate *time.Time, validAt *time.Time) bool { - // if validAt is nil, use the result from timeFunc (usually now) - at := timeFunc() - if validAt != nil { - at = *validAt - } - - // check if issuanceDate is before validAt - if issuanceDate.After(at.Add(maxSkew)) { - return false - } - - // check if expirationDate is after validAt - if expirationDate != nil && expirationDate.Add(maxSkew).Before(at) { - return false - } - return true -} - // Validate implements the Proof Verification Algorithm: https://w3c-ccg.github.io/data-integrity-spec/#proof-verification-algorithm func (v *verifier) Validate(credentialToVerify vc.VerifiableCredential, at *time.Time) error { err := v.validateType(credentialToVerify) @@ -221,7 +200,11 @@ func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrus } // Check issuance/expiration time - if !v.validateAtTime(credentialToVerify.IssuanceDate, credentialToVerify.ExpirationDate, validAt) { + validAtNotNil := time.Now() + if validAt != nil { + validAtNotNil = *validAt + } + if !credentialToVerify.ValidAt(validAtNotNil, maxSkew) { return types.ErrCredentialNotValidAtTime } @@ -353,7 +336,11 @@ func (v *verifier) validateJSONLDPresentation(presentation vc.VerifiablePresenta ldProof := ldProofs[0] // Validate signing time - if !v.validateAtTime(ldProof.Created, ldProof.Expires, validAt) { + at := timeFunc() + if validAt != nil { + at = *validAt + } + if !ldProof.ValidAt(at, maxSkew) { return toVerificationError(types.ErrPresentationNotValidAtTime) } diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 57746924dc..964ee07082 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -206,7 +206,8 @@ func Test_verifier_Validate(t *testing.T) { ctx := newMockContext(t) instance := ctx.verifier vc2 := testCredential(t) - vc2.IssuanceDate = time.Now() + issuanceDate := time.Now() + vc2.IssuanceDate = &issuanceDate ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(pk, nil) @@ -435,56 +436,6 @@ func TestVerifier_Verify(t *testing.T) { }) } -func Test_verifier_validateAtTime(t *testing.T) { - var timeToCheck *time.Time - t.Run("no time provided", func(t *testing.T) { - timeToCheck = nil - - t.Run("credential is valid", func(t *testing.T) { - sut := verifier{} - credentialToTest := testCredential(t) - valid := sut.validateAtTime(credentialToTest.IssuanceDate, credentialToTest.ExpirationDate, timeToCheck) - assert.True(t, valid) - }) - }) - - t.Run("with a time provided", func(t *testing.T) { - now := time.Now() - t.Run("credential is valid at given time", func(t *testing.T) { - timeToCheck = &now - sut := verifier{} - credentialToTest := testCredential(t) - valid := sut.validateAtTime(credentialToTest.IssuanceDate, credentialToTest.ExpirationDate, timeToCheck) - assert.True(t, valid) - }) - - t.Run("credential is invalid when timeAt is before issuance", func(t *testing.T) { - beforeIssuance, err := time.Parse(time.RFC3339, "2006-10-05T14:33:12+02:00") - require.NoError(t, err) - timeToCheck = &beforeIssuance - sut := verifier{} - credentialToTest := testCredential(t) - valid := sut.validateAtTime(credentialToTest.IssuanceDate, credentialToTest.ExpirationDate, timeToCheck) - assert.False(t, valid) - }) - - t.Run("credential is invalid when timeAt is after expiration", func(t *testing.T) { - expireTime, err := time.Parse(time.RFC3339, "2021-10-05T14:33:12+02:00") - require.NoError(t, err) - afterExpire := expireTime.Add(10 * time.Hour) - timeToCheck = &afterExpire - sut := verifier{} - credentialToTest := testCredential(t) - // Set expirationDate since the testCredential does not have one - credentialToTest.ExpirationDate = &expireTime - valid := sut.validateAtTime(credentialToTest.IssuanceDate, credentialToTest.ExpirationDate, timeToCheck) - assert.False(t, valid) - }) - - }) - -} - func Test_verifier_CheckAndStoreRevocation(t *testing.T) { rawVerificationMethod, _ := os.ReadFile("../test/revocation-public.json") rawRevocation, _ := os.ReadFile("../test/ld-revocation.json") diff --git a/vdr/didkey/resolver_test.go b/vdr/didkey/resolver_test.go index e6d1038568..f782f2744f 100644 --- a/vdr/didkey/resolver_test.go +++ b/vdr/didkey/resolver_test.go @@ -250,7 +250,7 @@ func TestResolver_Resolve(t *testing.T) { require.NoError(t, err) require.NotNil(t, doc) require.NotNil(t, md) - docJSON, _ := doc.MarshalJSON() + docJSON, _ := json.Marshal(doc) assert.JSONEq(t, expected, string(docJSON)) // Test the public key publicKey, err := doc.VerificationMethod[0].PublicKey() diff --git a/vdr/didnuts/ambassador_test.go b/vdr/didnuts/ambassador_test.go index c6d251aeda..d7df3c624d 100644 --- a/vdr/didnuts/ambassador_test.go +++ b/vdr/didnuts/ambassador_test.go @@ -244,7 +244,7 @@ func TestAmbassador_callback(t *testing.T) { // Document is missing context id, _ := did.ParseDID("did:foo:bar") emptyDIDDocument := did.Document{ID: *id} - didDocumentBytes, _ := emptyDIDDocument.MarshalJSON() + didDocumentBytes, _ := json.Marshal(emptyDIDDocument) err := ctx.ambassador.callback(tx, didDocumentBytes) diff --git a/vdr/didnuts/validators.go b/vdr/didnuts/validators.go index 2ef21cdc8c..ed10fddd2b 100644 --- a/vdr/didnuts/validators.go +++ b/vdr/didnuts/validators.go @@ -19,6 +19,7 @@ package didnuts import ( + "encoding/json" "errors" "fmt" "github.com/lestrrat-go/jwx/v2/jwk" @@ -130,7 +131,7 @@ type managedServiceValidator struct { func (m managedServiceValidator) Validate(document did.Document) error { // normalize services for consistent type checking. // TODO: this should probably happen somewhere else - bytes, err := document.MarshalJSON() + bytes, err := json.Marshal(document) if err != nil { return InvalidServiceError{err} } @@ -165,6 +166,8 @@ func (m managedServiceValidator) Validate(document did.Document) error { // RFC006 only allows maps or string, not sets. // Since service is not a map, and go-did normalizes everything to plurals, assume this is a string. resolvedEndpoint, err = m.resolveOrReturnEndpoint(service, cache) + case string: + resolvedEndpoint, err = m.resolveOrReturnEndpoint(service, cache) default: err = errors.New("invalid service format") } diff --git a/vdr/vdr.go b/vdr/vdr.go index b99f6247d1..212c241dc1 100644 --- a/vdr/vdr.go +++ b/vdr/vdr.go @@ -89,7 +89,7 @@ func (r *Module) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nuts return nil, resolver.ErrNotFound } - resultDIDDocumentData, _ := nutsDIDDocument.MarshalJSON() + resultDIDDocumentData, _ := json.Marshal(nutsDIDDocument) // Replace did:nuts DIDs with did:web, but only for owned DIDs webDID, err := didweb.URLToDID(*baseURL.JoinPath(nutsDID.ID)) if err != nil { diff --git a/vdr/vdr_test.go b/vdr/vdr_test.go index 208776bd65..62a312f717 100644 --- a/vdr/vdr_test.go +++ b/vdr/vdr_test.go @@ -624,7 +624,7 @@ func TestVDR_DeriveWebDIDDocument(t *testing.T) { }, } // remarshal expectedWebDIDDoc to make sure in-memory format is the same as the one returned by the API - data, _ := expectedWebDIDDoc.MarshalJSON() + data, _ := json.Marshal(expectedWebDIDDoc) _ = expectedWebDIDDoc.UnmarshalJSON(data) t.Run("ok", func(t *testing.T) {