Try   HackMD

Connecting a wallet to SUNETs SATOSA Credential Issuer.

The implementation and the setup follows version 0.7.1 of the Italian profile
https://github.com/italia/eudi-wallet-it-docs/tree/versione-corrente/docs/en

Environment setup

The environment consists of a small federation containing a Trust Anchor (TA), a Wallet provider (WP), a Trust Mark issuer (TMI) and the PID Issuer (PI).
All of these is expressed in two docker containers. The TA,TMI and WP in one and the PID Issuer in another.
See https://wiki.sunet.se/display/Projekt/EUDIW+pilot+setup for more information.

The Wallet and OpenID Federation

Even though a wallet, who wants to use the PID issuer provided by this setup, does not need to know OpenID Federation as a whole, it needs to know parts.

This includes sending a request to an endpoint and then being able to interpret the response.

In order to be able to do this the Wallet MUST:

  • be able to handle a signed JWT (JWS)(https://datatracker.ietf.org/doc/html/rfc7519)
    This includes being able to verify the signature of the JWS
  • be able to have access to information about the TA and Its keys.
  • be able to understand the format of the metadata for a federation entity and from that extract the URLs of the necessary endpoint.
  • be able to cryptographically sign data
  • have access to Elliptic Curve signing keys.

There is one services that the SUNET OpenID federation provides that the wallet MUST know about. That is resolver service. The resolve service does all the OpenID Federation steps for you:

  • collecting trust chains
  • verifying trust marks
  • applying metadata policies

and returns the result to you.

Wallet Instance Initialization and Registration

According to the Italian profile needs to do 5 steps to complete the Wallet Instance Initialization and Registration and 8 steps to complete the Wallet Attestation Issuance. I've paired that down to 4 :-)

  1. Find the URL to which the Wallet Instance request should be sent.
  2. Created the Wallet Instance request
  3. Sending the Wallet Instance request to the Wallet Provider
  4. Receive the Wallet Instance to the Wallet

In this scaled down version the wallet needs to know one endpoint expressed as an URL from the metadata for the Wallet Provider and that is the token_endpoint.

Find the URL to which the Wallet Instance request should be sent.

If it doesn't know how to collect and evaluate trust chains then the Wallet must use the resolve service provided by the trust anchor.

If we assume that the wallet knows no more than the entity ID of the Trust Anchor then it has to do this:

Now when the wallet knows the resolve endpoint it can do the next step in the process.

The pilot setup information tells you the entity ID of the Wallet Provider (https://openidfed-test-1.sunet.se:5001) so the wallet should pose the following query to the resolve endpoint using HTTP GET.

sub=https%3A%2F%2Fopenidfed-test-1.sunet.se%2F5001&
type=wallet_provider&
anchor=https%3A%2F%2openidfed-test-1.sunet.se%2F7001

The response is again a signed JWT. Since the Wallet has the public part of the TA's signing keys it should have no problem verifying the signatur on the response.

What the wallet wants out of the response is the token endpoint. Can be found under metadata/wallet_provider/token_endpoint.

Creating and sending the Wallet Instance request

The only necessary claims of the ones listed in the profile document are:

  • cnf (JSON object, containing the public part of an asymmetric key pair owned by the Wallet Instance.)
  • vp_format_supported (JSON object with name/value pairs, identifying a Credential format supported by the Wallet.)

The remaining claims:

  • challenge
  • hardware_signature
  • integrity_assertion
  • hardware_key_tag
  • authorization_endpoint
  • response_types_supported
  • response_modes_supported
  • request_object_signing_alg_values_supported

MUST all be assigned the value "__not__applicable__" . This signals to the wallet provider that it should work according to the scaled down version.

presentation_definition_uri_supported should be present with the value False.

The constructed JSON object should look something like this:

{
  "iss": "https://openidfed-test-1.sunet.se:5001/vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c",
  "challenge": "__not__applicable__",
  "hardware_signature": "__not__applicable__",
  "integrity_assertion": "__not__applicable__",
  "hardware_key_tag": "__not__applicable__",
  "cnf": {
    "jwk": {
      "crv": "P-256",
      "kty": "EC",
      "x": "4HNptI-xr2pjyRJKGMnz4WmdnQD_uJSq4R95Nj98b44",
      "y": "LIZnSB39vFJhYgS3k7jXE4r3-CoGFQwZtPBIRqpNlrg",
      "kid": "vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c"
    }
  },
  "vp_formats_supported": {
    "jwt_vc_json": {
      "alg_values_supported": [
        "ES256K",
        "ES384"
      ]
    },
    "jwt_vp_json": {
      "alg_values_supported": [
        "ES256K",
        "EdDSA"
      ]
    }
  },
  "iat": 1686645115,
  "exp": 1686652315
}

This JSON object should now be wrapped into a signed JWT and sent to the token_endpoint URL.

The token endpoint (as defined in RFC 7523 section 4) requires the following parameters encoded in application/x-www-form-urlencoded format:

  • grant_type set to urn:ietf:params:oauth:grant-type:jwt-bearer;
  • assertion containing the signed JWT of the Wallet Attestation Request.
POST /token HTTP/1.1
Host: openidfed-test-1.sunet.se
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6ImtoakZWTE9nRjNHeG...

The response (the Wallet Instance Attestation) JSON should look something like this:

{
  "iss": "https://openidfed-test-1.sunet.se:5001",
  "sub": "vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c",
  "aal": "something_or_other",
  "cnf": {
    "jwk": {
      "crv": "P-256",
      "kty": "EC",
      "x": "4HNptI-xr2pjyRJKGMnz4WmdnQD_uJSq4R95Nj98b44",
      "y": "LIZnSB39vFJhYgS3k7jXE4r3-CoGFQwZtPBIRqpNlrg",
      "kid": "vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c"
    }
  },
  "vp_formats_supported": {
    "jwt_vc_json": {
      "alg_values_supported": [
        "ES256K",
        "ES384"
      ]
    },
    "jwt_vp_json": {
      "alg_values_supported": [
        "ES256K",
        "EdDSA"
      ]
    }
  },
  "authorization_endpoint": "__not__applicable__",
  "response_types_supported": "__not__applicable__",
  "response_modes_supported": "__not__applicable__",
  "request_object_signing_alg_values_supported": "__not__applicable__",
  "presentation_definition_uri_supported": false,
  "iat": 1687281195,
  "exp": 1687288395
}

That taken care of we can go on to the conversation with the PID Issuer.

But first to summaries the initial steps:

  1. Fetch the Entity Configuration (EC) for the Trust Anchor (TA)
  2. From the TA's EC pick out the resolve endpoint
  3. Use the resolver to find the metadata for the Wallet Provider (WP)
  4. From the WP's metadata find the token endpoint
  5. Construct the Wallet Instance Request (WIR)
  6. Send the WIR to the WP's token endpoint
  7. Store the returned Wallet Instance Attestation (WIA)

Finding the PID Issuer

Eventually there will be a special endpoint that the SUNET setup provides. It's hosted at a entity called the query server. This seervice will traverse the federation and find all entities that match a given filter.
The filter consists of three parts:

The wallet can again collect the metadata for the found entity by using the resolver. The query server's service endpoint URL can be found at metadata/federation_entity/query_endpoint

Right now we can cheat and assume we have done the above and know that the PID Issuer can be found at https://satosa-test-1.sunet.se/

Getting the credential

A three step process. We use normal OAuth2 authorization if possible. Pushed Authorization is also available.

Authorization

Access token

Credential