[webauthn] Device-bound key extension (#1658)

agl has just created a new issue for https://github.com/w3c/webauthn:

== Device-bound key extension ==
In #1637 I wrote:

> As a measure to potentially address some of the challenges of introducing syncable credentials we have [floated](https://github.com/w3c/webauthn/issues/1546) the idea that syncable credentials may be paired with an automatically-generated, device-bound key pair. That would be a WebAuthn extension.

This is a proposal for that extension. This issue superceeds #1546 and so I'll close that in favour of this. This is mostly about the mechanism, but #1640 discusses some policy questions around this.

To use this extension (`devicePubKey`) an RP simply includes it (with the value `true`) on all create() and get() calls for their users. If the user employs a new-to-the-RP device for an authentication operation using a synced user credential, a new-to-the-RP device public key is returned, signaling the use of the new device.

Per the WebAuthn spec, if the devicePubKey extension is not supported by an authenticator, the extension is ignored, and no extension output is returned.

For both registration and authentication the underlying extension behavior is:

1. Create or select the [user's credential](https://www.w3.org/TR/webauthn-2/#credential-key-pair) as usual.
2. If a _device-bound key pair_ does not exist for this user credential and context, on the authenticator, create it (using the same public key algorithm as the user credential), otherwise recall the existing one.
3. Using the newly created or existing _device-bound key pair_, create a digital signature over a concatenation of the [hash of the serialized client data](https://www.w3.org/TR/webauthn-2/#collectedclientdata-hash-of-the-serialized-client-data) and the user credential ID.
4. Use the authenticator's AAGUID and the COSE_Key representation of the device public key (called "dpk") as inputs to the authenticator's [attestation statement format](https://www.w3.org/TR/webauthn/#sctn-defined-attestation-formats)'s "signing" and "verification" procedures, by substituting the AAGUID for authenticatorData and dpk for clientDataHash. Although some details vary between attestation statement formats, this approach appears to be workable for the applicable set of formats. (I.e. `packed`, `tpm`, `android-key`, `android-safetynet`, and `apple`.)
5. Include the authenticator's AAGUID, device public key (in COSE_Key format), the signature value calculated in step 3, along with the attestation statement created in step 4, in the devicePubKey extension output:

```
$$extensionOutput //= (                       ; Expressed in CDDL
 devicePubKey: AttObjForDevicePublicKey,
)

AttObjForDevicePublicKey = { ; Note: This object conveys an attested
                             ; device public key and is analogous to `attObj`.

  sig:     bstr,  ; result of sign((clientDataHash || userCredentialId),
                  ;                devicePrivateKey)
                  ; Note that this sig value is unique per-response
                  ; because the client data contains the per-request challenge.

  aaguid:  bstr,  ; authenticator's AAGUID (16 bytes fixed-length)
                  ; https://www.w3.org/TR/webauthn/#aaguid

  dpk:     bstr,  ; the Device Public Key (self-describing variable length,
                  ; COSE_Key format, CBOR-encoded)).

  ; whether this key is scoped to the whole device, or a loosely-defined,
  ; smaller scope called "app". For example, a "device"-scoped key is expected
  ; to be the same between an app and a browser on the same device, while
  ; an "app"-scoped key would probably not be.
  ;
  ; Whatever the scope, a device key is still specific to a given credential
  ; and does not provide any ability to link credentials.
  ;
  ; Whether device-scoped or not, keys are still device-bound. I.e. an
  ; app-scoped key does not enjoy lesser protection from extraction.
  context: "device" / "app"
  
  ; see https://www.w3.org/TR/webauthn/#sctn-generating-an-attestation-object
  ;
  ; Attestation statement formats define the `fmt` and `attStmt` members of
  ; $$attStmtType.
  ;
  ; In summary, the `attStmt` will (typically) contain:
  ;   (1) a SIGNATURE value calculated (using the attestation private key)
  ;       over (aaguid || dpk).
  ;   (2) the attestation certificate or public key, and supporting certificates, 
  ;       if any.  
  ;
  ; Note that there are details dependent upon the particular attestation
  ; statement format.
  ; See https://www.w3.org/TR/webauthn/#sctn-defined-attestation-formats.
  
  $$attStmtType, 
}
```

This extension output will itself be signed over in an "encompassing" attestation or assertion signature because extension outputs are an [authenticator data](https://www.w3.org/TR/webauthn/#authenticator-data) component. Also, this extension may be returned as a result of a get() on a device where no create() has been done, due to credential syncing.  

This design does not assign a "credential ID" to device keys because they are mapped to user credentials and we have the device private key sign over the client data hash and the user credential ID in order to "bind" to the current request and the user credential.

The device bound key pair is always of the same type (i.e., algorithm) as the user credential it is associated with.

We considered including a random authenticator-generated nonce in the data signed over by the device private key; the nominal notion being to randomize the signed-over data for side-channel protection. However, since this isn't done in CTAP so far, we have omitted it.

Consistently returning the _same_ device public key attestation object —  AttObjForDevicePublicKey — on _both_ registration and authentication operations simplifies the design and accommodates the case where a user credential was synced to a new device where there was no prior registration operation, requiring the creation and return of a new-to-the-RP device public key. Since this extension output is simultaneously serving as both a registration and a proof-of-possession of the device private key, it includes a signature by the device private key (as mentioned above).

The attestation statement signs over the AAGUID and device public-key values, i.e., without including clientDataHash. The rationale for not including clientDataHash is that this AttObjForDevicePublicKey.attStmt ought to be able to be calculated by the authenticator once (e.g., upon the user credential being synced to a new device), locally cached, and subsequently returned upon demand. Since CollectedClientData changes per-request, signing out it would make that impossible.

Thus an RP would only have to perform thorough validation of the device public key pair's attestation statement (using the aaguid and dpk values as inputs) once, and cache them along with the attestation statement. Then for subsequent .get() responses the RP is able to just do byte-level comparisons to verify that they are among the set of cached values. If not, then the RP is talking to a "new" device.

The devicePubKey extension may be supported by any platform authenticator or roaming authenticator having the resources necessary to support it.  Restricting this extension to only platform authenticators seems infeasible because the latter's definition is context dependent (see: phone-as-a-SK), and RPs may have various reasons, e.g., enterprise ones, for creating device key pairs on whatever authenticator form factor they encounter that is capable of supporting this extension.

(Thanks to @equalsJeffH, who actually wrote this proposal.)

Please view or discuss this issue at https://github.com/w3c/webauthn/issues/1658 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 4 August 2021 22:58:27 UTC