TalaoDAO / AltMe

Talao / Altme wallet : Open source Self Sovereign Identity wallet. Multi ecosystem support : EBSI conformant. ARF EUDI wallet implementation, DIIP and more.
https://talao.io
Apache License 2.0
48 stars 14 forks source link

Update of the wallet provider APIs / Initalisation, authentication and configuration update #2704

Open ThierryThevenet opened 5 months ago

ThierryThevenet commented 5 months ago

IN PROGRESS

This ticket is the first of a series to improve the current APIs between the wallet and the wallet provider back end with a 2FA authentication and allow the use of remote keys (HSM). Wallet uses a long life hardware key as first factor and a derivation of the user PIN as a second factor. In this process the wallet is not considered as a trusty device from the wallet provider standpoint and so after the initialization process, the PIN code is checked by the wallet provider back end for each authentication.

Generally speaking different keys are used in the wallet :

For this implementation, wallet attestation key will be a P-256 hardware device key.

The APIs specified in this ticket are :

Other tickets specify other APIs :

User can log as GUEST and in that case the login is an email starting with "guest@". As GUEST, user cannot access the following APIs :

Check the different flows https://swimlanes.io/u/Rt5vg-FWI

User initializes the wallet

0) User scans QR code provided by the wallet-provider back end to configure the wallet

Example : configuration://?login=thierry@talao.io&password=KJHGFT&walett-provider=https://wlallet-provider.talao.co

Password will be only used in this initialisation process but wallet provider URL and login must be saved and attached to the wallet instance life time.

1) Wallet calls the nonce endpoint

This API is opened to GUESTs.

From the wallet provider standpoint the nonce is the session identifier, the lifetime of the nonce is no more than 20 sec. The nonce is a 32 bytes random. This endpoint is exposed by the wallet provider back end without any protection but eventually with a rate limit.

GET /challenge HTTP/1.1
Host: wallet-provider.talao.co 
Accept: 'application/json'

response is a json

HTTP/1.1 200 OK
Content-Type: application/json

{
  "nonce": "d2JhY2NhbG91cmVqdWFuZGFt"
}

2) Wallet requests user to enter PIN code

For this process PIN code is checked by the wallet to be sure it is the PIN code of the app.

3) Wallet calls the initialisation endpoint

Parameters are added to the body of the POST request.

Example of the call

POST /initialisation HTTP/1.1
Host: wallet-provider.talao.co 
Content-Type: 'application/x-www-form-urlencoded'

 nonce=nonce
 &salted_pin_code=<hash256(PIN code + salt)
 &key_attestation=o2NmbXRvYXBwbGUtYXBw
 &login=thierry@talao.io
 &password=KJHGFT
 &jwk= '{
             "kty":"EC",
              "crv":"P256",
              "x":"BL2ueJxeRMpOdaqitOHeQQ0x5AnUBPG5nAzJa8D1KhQ",
              "y":"bHET7kx6NT9HgDmNXhRoi5_I6yegxQEd2qAcXQ_jdM-"
          }'

In response wallet receives the wallet attestation and the wallet configuration in a json :

HTTP/1.1 201 OK
Content-Type: application/json

{
    wallet-attestation" : "eyJhbGciOiIUYYUzI1NiIsInR5cCI6IndhbGx... ",
    wallet-configuration" : "eymkjhlkjhmgmjmkjh...."
}

The configuration is a jwt signed by the wallet provider back end.

The configuration file has a new attribute walletFor to specify if the wallet is used as an individual wallet (default as today) or a business wallet. "walletFor": "np" for natural person (individual wallet) "walletFor": "lp" for legal person (business wallet)

Example of an extract of configuration file:

{
    "version": "2.0",
    "generalOptions": {
        "walletType": "altme",
        "walletFor": "np",
        "companyName": "Altme",
        .....

For Business wallets (legal person) the data needed for the wallet is provided in the companySignature nested json of the configuration file

Example of an extract of the configuration file for a company using a DID on Ethereum


  "companySignature": {
        "client_id": "did:ethr:0x1356744765ACD56756",
         "kid": "did:ethr:0x1356744765ACD56756#key-1",
         "jwk" : null
    },

Example of an extract of the configuration file for a company using a HSM key


  "companySignature": {
        "client_id": "talao",
         "kid": "mjhmkjh65765Mlk",
          "jwk" : {
             "kty":"EC",
              "crv":"P256",
              "x":"BL2ueJxeRMpOdaqitOHeQQ0x5AnUBPG5nAzJa8D1KhQ",
              "y":"bHET7kx6NT9HgDmNXhRoi5_I6yegxQEd2qAcXQ_jdM-"
        }
    },

The wallet attestation is a jwt signed by the wallet provider back end.

Example of a wallet attestation signed by the wallet provider backend.

  {
  "alg": "ES256",
  "kid": "5t5YYpBhN-EgIEEI5iUzr6r0MR02LnVQ0OmekmNKcjY",
  "x5c": [....],
  "typ": "wallet-attestation+jwt",
}
.

{
  "iss": "https://wallet-provider.example.org",
  "sub": "vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c",
  "jti": "urn:123456",
   "status": {
            "status_list": {
                "idx": 45678,
                "uri": "https://talao.co/sandbox/issuer/statuslist/1"
            }
        },
  "cnf":
  {
    "jwk":
    {
      "crv": "P-256",
      "kty": "EC",
      "x": "4HNptI-xr2pjyRJKGMnz4WmdnQD_uJSq4R95Nj98b44",
      "y": "LIZnSB39vFJhYgS3k7jXE4r3-CoGFQwZtPBIRqpNlrg",
      "kid": "vbeXJksM45xphtANnCiG6mCyuU4jfGNzopGuKvogg9c"
    }
  },
  "key_type": "software",
  "user_authentication": "system_biometry",
  "authorization_endpoint": "https://app.altme.io/app/download/authorize",
  "response_types_supported": [
    "vp_token"
  ],
  "response_modes_supported": [
    "form_post.jwt"
  ],
  "vp_formats_supported": {
      "vc+sd-jwt": {
          "sd-jwt_alg_values": [
              "ES256",
              "ES384"
          ]
      }
  },
  "request_object_signing_alg_values_supported": [
    "ES256"
  ],
  "presentation_definition_uri_supported": false,
  "iat": 1687281195,
  "exp": 1687288395
}

Wallet provider back end must check login/password of the user and associate hardware key for authentication and salted PIN code to the user data.

Wallet authenticates with the wallet provider backend

This API is opened to GUESTs.

0) Wallet requests to user the PIN code

1) Wallet calls the token endpoint

Wallet uses client_secret_jwt method to authenticate see https://datatracker.ietf.org/doc/html/rfc7523 to get a bearer token bound to the wallet provider authentication key. The jwt is called assertion below and provides the hardware key as first factor and the salted pin code for second factor.

Wallet uses the hardware P-256 key generated at initialisation to manage the authentication with the wallet provider back end. It will be used to get a token each time the wallet needs to access the wallet provider back end services. The token issued through this endpoint is bound to that key for future use. This key is the attribute jwk of the assertion payload. The assertion is signed with this key through the secure element of the smartphone.

The PIN code + salt is transformed by sha256 and added to the wallet assertion 'salted_pin_code as previously. The salted pin code is not stored in the wallet.

The user login is passed in the assertion, password is not needed.

The life time of the assertion is no more than 10 sec (exp - iat).

POST /token HTTP/1.1
Host: wallet-provider.talao.co 
Content-Type: 'application/x-www-form-urlencoded'

client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer".
&client_assertion = <assertion>

Example of an assertion payload and header

{
        "alg": "ES256",
         "typ": "JWT",
         "jwk": {
             "kty":"EC",
              "crv":"P256",
              "x":"BL2ueJxeRMpOdaqitOHeQQ0x5AnUBPG5nAzJa8D1KhQ",
              "y":"bHET7kx6NT9HgDmNXhRoi5_I6yegxQEd2qAcXQ_jdM-"
          }
}

{
        "iss": <client_id>,
         "sub": <client_id>,
         "aud": "https://wallet-provider.talao.co",
        "iat": 1717590924,
        "exp": 1717700924,
        "nonce": <nonce>,
        "salted_pin_code" : <hash256(PIN code + salt)>,
        "login"; "thierry@altme.io"
 }

Wallet receives a short life bearer token bound to its key. The life time of the token is 5 minutes.

NB : if the token is a jwt it must be encrypted (jwe).


HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

  {
    "access_token": "JhbGciOiJSUzI1NiIsInR5cCI6sHQ",
    "token_type": "bearer",
    "expires_in": 300,
}

If the salted_pin_code or authentication key do not fit with what has been stored the user data base at initialisation, the token endpoint return a 401 unauthorized response. User can make 3 trials maximum with new PIN code.

For GUEST, the wallet provider back end does not need to check the salted_pin_code and authentication key as they are not stored but the issued token will be restricted to a few APIs.

User updates wallet configuration

This API is opened to GUESTs.

0) User clics on "Update the configuration" 1) Wallet calls nonce endpoint 2) Wallet authenticates to the wallet provider back end to get a bearer token 3) Wallet calls /update-configuration endpoint with the bearer token and a DPoP

DPoP (Demonstrating Proof of Possession) is a jwt transferred through the header of the POST request. It is used to avoid a leak of the access token as it proves that the wallet still owns the key attached to the Bearer token. DPoP life time is very short (10 sec). DPoP is recalculated each time as it is single use.

See DPoP specifications if needed : https://www.rfc-editor.org/rfc/rfc9449.html

DPoP specific attributes : jwk: the key used to get the token (wallet provider back end authentication key) htm: "POST" htu: url of the endpoint (https://wallet-provider.talao.co/update-configuration) ath: the sha256 hash of the access token

DPoP is signed by the wallet provider back end authentication key.

Example of the DPoP :

{
      "alg": "ES256"
      "typ": "dpop+jwt"
      "jwk": {
             "kty":"EC",
              "crv":"P256",
              "x":"BL2ueJxeRMpOdaqitOHeQQ0x5AnUBPG5nAzJa8D1KhQ",
              "y":"bHET7kx6NT9HgDmNXhRoi5_I6yegxQEd2qAcXQ_jdM-"
          }
}
.
{
      "ath": < hash sh256 of the access token>
      "htm": "POST",
      "htu": "https://wallet-provider.talao.co/update-configuration",
      "exp":1300819380,
      "jti": <random>
}

No data are transferred in the body of the call for this endpoint.

Example of the call to the update-configuration endpoint :

POST /update-configuration HTTP/1.1
Host: wallet-provider.talao.co 
Authorization: Bearer <token>
DPoP : eylkjglkjhdlkjhlkjhhjkljhlkjhlkjhgvsmkjhlkjlkj...
Content-Type: 'application/x-www-form-urlencoded'

Wallet provider back end responds with new configuration (200) or unauthorized (401) .

Example :

HTTP/1.1 200 OK
Content-Type: application/jwt

eyJhbGciOiJFUzI1NiIsInR5cCI6IndhbGx... 

Wallet applies the new configuration.

patatoid commented 5 months ago

I think using bearer tokens force to perform authorization at each signature, since we do not have an OAuth server that would help to obtain such tokens, using the instance key pair for authorization would be better.

The securitization of the endpoints may be made using DPoP at first, adding the pin code later on. https://datatracker.ietf.org/doc/html/rfc9449 making the requests looking like:

POST /sign HTTP/1.1
Host: wallet-provider.talao.co 
DPoP: eyJ0eXAiOiJk...
Content-Type: 'application/x-www-form-urlencoded'

message = <base64 url safe (message)>

where the DPoP would be signed with the instance private key and verified with the public key obtained while performing the wallet attestation flow. The payload of the DPoP looking like:

{
  "jti":"-BwC3ESc6acc2lTc",
  "htm":"POST",
  "htu":"https://wallet-provider.talao.co/sign",
  "iat":1562262616
}

We would even add the salted pin code to this which would help by having a second factor.

The wallet attestation flow is from a standard, we should keep it as close to the specification as possible.

ThierryThevenet commented 5 months ago

utiliser une autre clé que celle de l attestation pour l'authentificaion au portail pour eventuellement pourvoir gerer des attestations de wallet single use (eudi wallet np)

ThierryThevenet commented 5 months ago

remove the basic aith as the login/password could be changed by user with wallet

ThierryThevenet commented 5 months ago

Probleme du GUEST