Making a third-party credential available on multiple devices by issuing it to the user’s sync fabric

In the previous post I went over a live demonstration showing how a passkey can be used for replication of a credential comprising a JSON certificate and its associated private key across browser instances on different devices controlled by the same user.

Here I propose a new method, not yet implemented, of using a passkey to make such a credential available on multiple devices. In this method the credential is issued to the user's sync fabric as a whole, instead of being replicated across browser instances after being issued to one particular instance. After being issued to the fabric, it is available on any browser instance with full cryptographic protection, instead of being stored in the clear in each instance where it has been replicated. By the user's sync fabric I mean the set of browser instances to which the passkey can be synced. By full cryptographic protection I mean that the certificate is encrypted and the private key is in protected storage such as the TPM used by Windows Hello.

The new method uses the following issuance and presentation protocols.

Issuance to the sync fabric

  1. As in the previous post, the credential issuer creates a database record with verified attributes for a particular user, indexed by a randomly generated retrieval token. Here the size of the token is 32 bytes.
  2. Again as in the previous post, the issuer provides the retrieval token to the user by including it in a credential issuance URL made available as a QR code or sent by email.
  3. The user launches the credential issuance URL on a browser instance controlled by the user (e.g. an instance of Chrome on a Windows device where the user is logged in to Google).
  4. The issuer frontend calls navigator.credentials.create() to create a passkey with a 64-byte user.id parameter (the maximum size of that parameter) consisting of a concatenation of the 32-byte retrieval token and a random 32-byte AES key.
  5. The issuer frontend extracts the public key of the passkey from the response to the create() call.
  6. The issuer frontend uses the retrieval token to retrieve the verified attributes and asks the user to choose the attributes to be included in the certificate.
  7. The issuer frontend sends the retrieval token, the chosen attributes, the public key of the passkey, and the AES key to the issuer backend.
  8. The issuer backend checks that the chosen attributes are in the database record of verified attributes indexed by the retrieval token, then creates a certificate binding the attributes to the public key of the passkey, signs the certificate, and encrypts it with the AES key.
  9. The issuer backend adds the encrypted certificate as an additional field to the database record indexed by the retrieval token.
  10. The user's sync fabric is now equipped with a cryptographically protected credential, comprising the encrypted JSON certificate stored in the database and the private key component of the passkey, that can be presented to relying parties accessed from any of the browser instances in the fabric as shown below.

Presentation to a relying party

  1. The RP sends a credential presentation request to the credential issuer on a browser instance controlled by the user, which may or may not have been used before for credential presentation to the same relying party. If it has been used before, there may be an encrypted certificate in localStorage and a service worker that will intercept the request to the credential issuer. If it has not been used before there will be no service worker and no encrypted certificate.
  2. If there is no service worker the request reaches the credential issuer, which handles it as follows:
    1. The issuer frontend asks the user for consent to present the credential to the RP, with the option of creating a service worker and storing the encrypted certificate in localStorage to facilitate subsequent presentations.
    2. The frontend calls navigator.credentials.get() requesting a signature on the challenge received from the RP and the callback URL.
    3. The frontend obtains the signature and the userHandle with the retrieval token and the AES key from the response to the get() call.
    4. The frontend downloads the encrypted certificate from the database record indexed by the retrieval token.
    5. The frontend creates a service worker and stores the encrypted certificate in localStorage, if the option to do so was not declined above.
    6. The frontend uses the AES key to decrypt the encrypted certificate and sends the signature and the decrypted certificate to the RP.
    7. The RP verifies the signature using the public key in the certificate, which is the public key component of the passkey, and validates the certificate as in the previous post.
  3. If there is a service worker, in which case there will also be an encrypted certificate in local storage, the request is intercepted and handled as follows:
    1. The service worker creates a consent page.
    2. JavaScript code in the consent page calls navigator.credentials.get() requesting a signature on the challenge received from the RP and the callback URL.
    3. The JavaScript code obtains the signature, and the AES key in the userHandle.
    4. The JavaScript code uses the AES key to decrypt the encrypted certificate and sends the signature and the certificate to the RP.
    5. The RP verifies the signature using the public key in the certificate, which is the public key component of the passkey, and validates the certificate as in the previous post.

Leave a Reply

Your email address will not be published. Required fields are marked *