Skip to content

support verification with gaia-x registry #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 94 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,18 @@ configRepo:
trustedParticipants:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- https://tir-pdc.gaia-x.fiware.dev
- https://tir-pdc.ebsi.fiware.dev
# the credentials type to configure the endpoint(s) for
CustomerCredential:
- https://tir-pdc.gaia-x.fiware.dev
- https://tir-pdc.ebsi.fiware.dev
# trusted issuers endpoint configuration
trustedIssuers:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- https://tir-pdc.gaia-x.fiware.dev
- https://tir-pdc.ebsi.fiware.dev
# the credentials type to configure the endpoint(s) for
CustomerCredential:
- https://tir-pdc.gaia-x.fiware.dev
- https://tir-pdc.ebsi.fiware.dev

```
#### Templating
Expand Down Expand Up @@ -208,6 +208,96 @@ which will be answered with(demo jwt, will be signed in reality):
}
```

## Trust Anchor Integration

The Verifier currently supports 2 types of Participant Lists:

* [EBSI Trusted Issuers Registry API](https://hub.ebsi.eu/apis/conformance/trusted-issuers-registry)
* [GAIA-X Registry](https://gitlab.com/gaia-x/lab/compliance/gx-registry)

> :bulb: The following example configurations are provided through the static yaml file. Its recommended to use the [Credentials-Config-Service](https://github.com/FIWARE/credentials-config-service) instead, to have the ability for dynamic changes. All described configurations are supported by the service in version >=2.0.0

### EBSI TIR

In order to check an issuer against an EBSI Trusted Issuers Registry, it needs to be configured for the supported credentials. When using the file config, it would look like:

```yaml
configRepo:
# static configuration for services
services:
# name of the service to be configured
testService:
# scope to be requested from the wallet
scope:
- VerifiableCredential
# trusted participants endpoint configuration
trustedParticipants:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- type: ebsi
url: https://tir-pdc.ebsi.fiware.dev
```

For backward compatibility, the EBSI List is the default at the moment, thus the following (simplified) configuration is also valid:

```yaml
configRepo:
# static configuration for services
services:
# name of the service to be configured
testService:
# scope to be requested from the wallet
scope:
- VerifiableCredential
# trusted participants endpoint configuration
trustedParticipants:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- https://tir-pdc.ebsi.fiware.dev
```

### Gaia-X Registry

When using the [Gaia-X Digital Clearing House's](https://gaia-x.eu/services-deliverables/digital-clearing-house/) Registry Services, the issuer to be checked needs to fullfill the requirements of a Gaia-X participant. Thus, only did:web is supported for such and they need to provide a valid ```x5u``` location as part of their ```publicKeyJwk```. Usage of such registries can than be configured as following:
```yaml
configRepo:
# static configuration for services
services:
# name of the service to be configured
testService:
# scope to be requested from the wallet
scope:
- VerifiableCredential
# trusted participants endpoint configuration
trustedParticipants:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- type: gaia-x
url: https://registry.lab.gaia-x.eu
```

### Mixed usage

Its also possible to trust multiple list with different types. In this case, the issuer is trusted if its found in at least one of the lists. Configuration would be as following:
```yaml
configRepo:
# static configuration for services
services:
# name of the service to be configured
testService:
# scope to be requested from the wallet
scope:
- VerifiableCredential
# trusted participants endpoint configuration
trustedParticipants:
# the credentials type to configure the endpoint(s) for
VerifiableCredential:
- type: ebsi
url: https://tir-pdc.ebsi.fiware.dev
- type: gaia-x
url: https://registry.lab.gaia-x.eu
```

## API

The API implements enpoints defined in [OIDC4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-terminology) and [SIOP-2](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html). The OpenAPI Specification of the implemented endpoints can be found at: [api/api.yaml](api/api.yaml).
Expand All @@ -216,7 +306,6 @@ The API implements enpoints defined in [OIDC4VP](https://openid.net/specs/openid

The VCVerifier does currently not support all functionalities defined in the connected standards(e.g. [OIDC4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-terminology) and [SIOP-2](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html)). Users should be aware of the following points:

* the verifier does not yet verify the holder of a credential
* the verifier does not offer any endpoint to proof its own identity
* requests to the authentication-response endpoint do accept "presentation_submissions", but do not evaluate them
* even thought the vp_token can contain multiple credentials and all of them will be verified, just the first one will be included in the JWT
Expand Down
9 changes: 8 additions & 1 deletion config/configClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,20 @@ type Credential struct {
// Type of the credential
Type string `json:"type" mapstructure:"type"`
// A list of (EBSI Trusted Issuers Registry compatible) endpoints to retrieve the trusted participants from.
TrustedParticipantsLists []string `json:"trustedParticipantsLists,omitempty" mapstructure:"trustedParticipantsLists,omitempty"`
TrustedParticipantsLists []TrustedParticipantsList `json:"trustedParticipantsLists,omitempty" mapstructure:"trustedParticipantsLists,omitempty"`
// A list of (EBSI Trusted Issuers Registry compatible) endpoints to retrieve the trusted issuers from. The attributes need to be formated to comply with the verifiers requirements.
TrustedIssuersLists []string `json:"trustedIssuersLists,omitempty" mapstructure:"trustedIssuersLists,omitempty"`
// Configuration of Holder Verfification
HolderVerification HolderVerification `json:"holderVerification" mapstructure:"holderVerification"`
}

type TrustedParticipantsList struct {
// Type of praticipants list to be used - either gaia-x or ebsi
Type string `json:"type" mapstructure:"type"`
// url of the list
Url string `json:"url" mapstructure:"url"`
}

type HolderVerification struct {
// should holder verification be enabled
Enabled bool `json:"enabled" mapstructure:"enabled"`
Expand Down
4 changes: 2 additions & 2 deletions config/configClient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func Test_getServices(t *testing.T) {
"did_write": {
{
Type: "VerifiableCredential",
TrustedParticipantsLists: []string{"https://tir-pdc.gaia-x.fiware.dev"},
TrustedIssuersLists: []string{"https://til-pdc.gaia-x.fiware.dev"},
TrustedParticipantsLists: []TrustedParticipantsList{{Type: "ebsi", Url: "https://tir-pdc.ebsi.fiware.dev"}},
TrustedIssuersLists: []string{"https://til-pdc.ebsi.fiware.dev"},
HolderVerification: HolderVerification{Enabled: false, Claim: "subject"},
},
},
Expand Down
7 changes: 5 additions & 2 deletions config/data/ccs_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
{
"type": "VerifiableCredential",
"trustedParticipantsLists": [
"https://tir-pdc.gaia-x.fiware.dev"
{
"type": "ebsi",
"url": "https://tir-pdc.ebsi.fiware.dev"
}
],
"trustedIssuersLists": [
"https://til-pdc.gaia-x.fiware.dev"
"https://til-pdc.ebsi.fiware.dev"
],
"holderVerification": {
"enabled": false,
Expand Down
5 changes: 3 additions & 2 deletions config/data/config_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ configRepo:
someScope:
- type: VerifiableCredential
trustedParticipantsLists:
- https://tir-pdc.gaia-x.fiware.dev
- type: ebsi
url: https://tir-pdc.ebsi.fiware.dev
trustedIssuersLists:
- https://til-pdc.gaia-x.fiware.dev
- https://til-pdc.ebsi.fiware.dev

4 changes: 2 additions & 2 deletions config/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ func Test_ReadConfig(t *testing.T) {
"someScope": {
{
Type: "VerifiableCredential",
TrustedParticipantsLists: []string{"https://tir-pdc.gaia-x.fiware.dev"},
TrustedIssuersLists: []string{"https://til-pdc.gaia-x.fiware.dev"},
TrustedParticipantsLists: []TrustedParticipantsList{{Type: "ebsi", Url: "https://tir-pdc.ebsi.fiware.dev"}},
TrustedIssuersLists: []string{"https://til-pdc.ebsi.fiware.dev"},
},
},
},
Expand Down
121 changes: 121 additions & 0 deletions gaiax/gaiaXClient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package gaiax

import (
"bytes"
"encoding/json"
"errors"
"net/http"
"strings"

"github.com/fiware/VCVerifier/common"
"github.com/fiware/VCVerifier/logging"
"github.com/trustbloc/did-go/doc/did"
"github.com/trustbloc/did-go/method/web"
"github.com/trustbloc/did-go/vdr"
vdrapi "github.com/trustbloc/did-go/vdr/api"
)

const GAIAX_REGISTRY_TRUSTANCHOR_FILE = "/v2/api/trustAnchor/chain/file"

var ErrorUnresolvableDid = errors.New("unresolvable_did")

/**
* A client to retrieve infromation from EBSI-compatible TrustedIssuerRegistry APIs.
*/
type GaiaXClient interface {
IsTrustedParticipant(registryEndpoint string, did string) (trusted bool)
}

type GaiaXHttpClient struct {
client common.HttpClient
didRegistry vdrapi.Registry
}

func NewGaiaXHttpClient() (client GaiaXClient, err error) {
return GaiaXHttpClient{client: &http.Client{}, didRegistry: vdr.New(vdr.WithVDR(web.New()))}, nil
}

func (ghc GaiaXHttpClient) IsTrustedParticipant(registryEndpoint string, did string) (trusted bool) {

logging.Log().Debugf("Verify participant %s at gaia-x registry %s.", did, registryEndpoint)

// 1. get jwk from did
didDocument, err := ghc.resolveIssuer(did)

if err != nil {
logging.Log().Warnf("Was not able to resolve the issuer %s. E: %v", did, err)
return false
}

// 2. verify at the registry
for _, verficationMethod := range didDocument.DIDDocument.VerificationMethod {
if verficationMethod.ID == did {
logging.Log().Debugf("Verify the issuer %s.", did)
return ghc.verifiyIssuer(registryEndpoint, verficationMethod)
}
}

return false
}

func (ghc GaiaXHttpClient) verifiyIssuer(registryEndpoint string, verificationMethod did.VerificationMethod) (trusted bool) {
jwk := verificationMethod.JSONWebKey()

if jwk.CertificatesURL != nil {
return ghc.verifyFileChain(registryEndpoint, jwk.CertificatesURL.String())
}
// gaia-x did-json need to provide an x5u, thus x5c checks are not required.
return false
}

func (ghc GaiaXHttpClient) verifyFileChain(registryEndpoint string, x5u string) (trusted bool) {
requestBody := FileChainRequest{Uri: x5u}

encodedRequest, err := json.Marshal(requestBody)
if err != nil {
logging.Log().Warnf("Was not able to build a valid certificate check bode. E: %v", err)
return false
}

request, _ := http.NewRequest("POST", buildURL(registryEndpoint, GAIAX_REGISTRY_TRUSTANCHOR_FILE), bytes.NewBuffer(encodedRequest))
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")

response, err := ghc.client.Do(request)
if err != nil {
logging.Log().Infof("Was not able to check cert chain %s at %s. E: %v", x5u, registryEndpoint, err)
return false
}
defer response.Body.Close()
if response.StatusCode != 200 {
logging.Log().Infof("x5u %s was not verified to be a trust anchor at %s. Response: %v", x5u, registryEndpoint, response.StatusCode)
return false
}
// according to the spec, all 200s are valid chains, thus no need to parse the body
return true
}

func (ghc GaiaXHttpClient) resolveIssuer(did string) (didDocument *did.DocResolution, err error) {
didDocument, err = ghc.didRegistry.Resolve(did)
if err != nil {
logging.Log().Warnf("Was not able to resolve the issuer %s.", did)
return nil, ErrorUnresolvableDid
}
return didDocument, err
}

func buildURL(host, path string) string {
return strings.TrimSuffix(host, "/") + "/" + strings.TrimPrefix(path, "/")
}

type FileChainRequest struct {
Uri string `json:"uri"`
}

type CertChainRequest struct {
Certs string `json:"certs"`
}

type VerificationResult struct {
Result bool `json:"result"`
}
Loading
Loading