Skip to content

Commit

Permalink
Discovery service retraction fix (#3498)
Browse files Browse the repository at this point in the history
* broken test

* fix retraction

* move retraction type from .type to .vp.type
  • Loading branch information
gerardsn authored Oct 18, 2024
1 parent 7d8da8c commit de4f587
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 12 deletions.
16 changes: 10 additions & 6 deletions discovery/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func (r *defaultClientRegistrationManager) deactivate(ctx context.Context, servi
func (r *defaultClientRegistrationManager) deregisterPresentation(ctx context.Context, subjectDID did.DID, service ServiceDefinition, vp vc.VerifiablePresentation) error {
presentation, err := r.buildPresentation(ctx, subjectDID, service, nil, map[string]interface{}{
"retract_jti": vp.ID.String(),
})
}, &retractionPresentationType)
if err != nil {
return err
}
Expand Down Expand Up @@ -263,15 +263,18 @@ func (r *defaultClientRegistrationManager) findCredentialsAndBuildPresentation(c
return nil, fmt.Errorf(errStr, service.ID, subjectDID, err)
}

return r.buildPresentation(ctx, subjectDID, service, matchingCredentials, nil)
return r.buildPresentation(ctx, subjectDID, service, matchingCredentials, nil, nil)
}

func (r *defaultClientRegistrationManager) buildPresentation(ctx context.Context, subjectDID did.DID, service ServiceDefinition,
credentials []vc.VerifiableCredential, additionalProperties map[string]interface{}) (*vc.VerifiablePresentation, error) {
func (r *defaultClientRegistrationManager) buildPresentation(ctx context.Context, subjectDID did.DID, service ServiceDefinition, credentials []vc.VerifiableCredential, additionalProperties map[string]interface{}, additionalVPType *ssi.URI) (*vc.VerifiablePresentation, error) {
nonce := nutsCrypto.GenerateNonce()
// Make sure the presentation is not valid for longer than the max validity as defined by the Service Definitio.
expires := time.Now().Add(time.Duration(service.PresentationMaxValidity-1) * time.Second).Truncate(time.Second)
holderURI := subjectDID.URI()
var additionalVPTypes []ssi.URI
if additionalVPType != nil {
additionalVPTypes = append(additionalVPTypes, *additionalVPType)
}
return r.vcr.Wallet().BuildPresentation(ctx, credentials, holder.PresentationOptions{
ProofOptions: proof.ProofOptions{
Created: time.Now(),
Expand All @@ -280,8 +283,9 @@ func (r *defaultClientRegistrationManager) buildPresentation(ctx context.Context
Nonce: &nonce,
AdditionalProperties: additionalProperties,
},
Format: vc.JWTPresentationProofFormat,
Holder: &holderURI,
AdditionalTypes: additionalVPTypes,
Format: vc.JWTPresentationProofFormat,
Holder: &holderURI,
}, &subjectDID, false)
}

Expand Down
6 changes: 5 additions & 1 deletion discovery/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,11 @@ func Test_defaultClientRegistrationManager_deactivate(t *testing.T) {
t.Run("registered", func(t *testing.T) {
ctx := newTestContext(t)
ctx.invoker.EXPECT().Register(gomock.Any(), gomock.Any(), gomock.Any())
ctx.wallet.EXPECT().BuildPresentation(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).Return(&vpAlice, nil)
ctx.wallet.EXPECT().BuildPresentation(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).DoAndReturn(
func(ctx context.Context, credentials []vc.VerifiableCredential, options holder.PresentationOptions, signerDID *did.DID, validateVC bool) (*vc.VerifiablePresentation, error) {
assert.Equal(t, options.AdditionalTypes[0], retractionPresentationType)
return &vpAlice, nil // not a revocation VP
})
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), aliceSubject).Return([]did.DID{aliceDID}, nil)
_, err := ctx.store.add(testServiceID, vpAlice, testSeed, 1)
require.NoError(t, err)
Expand Down
8 changes: 6 additions & 2 deletions discovery/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,15 @@ func (m *Module) validateRegistration(definition ServiceDefinition, presentation
}

func (m *Module) validateRetraction(serviceID string, presentation vc.VerifiablePresentation) error {
// Presentation might be a retraction (deletion of an earlier credentialRecord) must contain no credentials, and refer to the VP being retracted by ID.
// If those conditions aren't met, we don't need to register the retraction.
// RFC022 §3.4:it MUST specify RetractedVerifiablePresentation as type, in addition to the VerifiablePresentation.
// presentation.IsType(retractionPresentationType) // satisfied by the switch one level up

// RFC022 §3.4: it MUST NOT contain any credentials.
if len(presentation.VerifiableCredential) > 0 {
return errRetractionContainsCredentials
}

// RFC022 §3.4: it MUST contain a retract_jti JWT claim, containing the jti of the presentation to retract.
// Check that the retraction refers to an existing presentation.
// If not, it might've already been removed due to expiry or superseded by a newer presentation.
retractJTIRaw, _ := presentation.JWT().Get("retract_jti")
Expand Down
48 changes: 45 additions & 3 deletions e2e-tests/discovery/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ fi
echo "---------------------------------------"
echo "Registering care organization on Discovery Service..."
echo "---------------------------------------"
curl --insecure -s -X POST http://localhost:28081/internal/discovery/v1/dev:eOverdracht2023/${SUBJECT}
RESPONSE=$(curl --insecure -s -X POST http://localhost:28081/internal/discovery/v1/dev:eOverdracht2023/${SUBJECT})
if [ -z "${RESPONSE}" ]; then
echo "Registered for service"
else
echo "FAILED: Could not register for Discovery Service" 1>&2
exitWithDockerLogs 1
fi

# Registration refresh interval is 500ms, wait some to make sure the registration is refreshed
sleep 2
Expand All @@ -68,8 +74,6 @@ fi
echo "---------------------------------------"
echo "Searching for care organization registration on Discovery Client..."
echo "---------------------------------------"
# Service refresh interval is 500ms, wait some to make sure the presentations are loaded
sleep 2
RESPONSE=$(curl -s --insecure "http://localhost:28081/internal/discovery/v1/dev:eOverdracht2023?credentialSubject.organization.name=Care*")
NUM_ITEMS=$(echo $RESPONSE | jq length)
if [ $NUM_ITEMS -eq 1 ]; then
Expand All @@ -87,6 +91,44 @@ else
exitWithDockerLogs 1
fi

echo "---------------------------------------"
echo "Retract Discovery Service registration..."
echo "---------------------------------------"
RESPONSE=$(curl --insecure -s -X DELETE http://localhost:28081/internal/discovery/v1/dev:eOverdracht2023/${SUBJECT})
if [ -z "${RESPONSE}" ]; then
echo "Registration revoked"
else
echo "FAILED: Registration not (immediately) revoked" 1>&2
exitWithDockerLogs 1
fi

# Registration refresh interval is 500ms, wait some to make sure the registration is refreshed
sleep 2

echo "---------------------------------------"
echo "Searching for care organization registration on Discovery Server..."
echo "---------------------------------------"
RESPONSE=$(curl -s --insecure "http://localhost:18081/internal/discovery/v1/dev:eOverdracht2023?credentialSubject.organization.name=Care*")
NUM_ITEMS=$(echo $RESPONSE | jq length)
if [ $NUM_ITEMS -eq 0 ]; then
echo "Registration not found"
else
echo "FAILED: Found registration" 1>&2
exitWithDockerLogs 1
fi

echo "---------------------------------------"
echo "Searching for care organization registration on Discovery Client..."
echo "---------------------------------------"
RESPONSE=$(curl -s --insecure "http://localhost:28081/internal/discovery/v1/dev:eOverdracht2023?credentialSubject.organization.name=Care*")
NUM_ITEMS=$(echo $RESPONSE | jq length)
if [ $NUM_ITEMS -eq 0 ]; then
echo "Registration not found"
else
echo "FAILED: Found registration" 1>&2
exitWithDockerLogs 1
fi

echo "------------------------------------"
echo "Stopping Docker containers..."
echo "------------------------------------"
Expand Down

0 comments on commit de4f587

Please sign in to comment.