[webauthn] Proposal/discussion: getting a non-extractable CryptoKey result from the prf extension (#1895)

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

== Proposal/discussion: getting a non-extractable CryptoKey result from the prf extension ==
# Background
The `prf` extension creates a path for web applications to support end-to-end encryption via hardware authenticators in the browser, in conjunction with [WebCrypto](https://github.com/w3c/webcrypto). [Encrypting Data in the Browser Using WebAuthn](https://blog.millerti.me/2023/01/22/encrypting-data-in-the-browser-using-webauthn/) presents some sample code for how this can be achieved in Chrome Canary today.

From reading discussion around prf, E2EE appears to be one of the core use cases, e.g. for password managers or any applications written with a focus on user privacy. (In my case, I'm developing a PWA that does all user data handling in the browser with encrypted backups.)

## Problem
When crossing the webauthn <> webcrypto api boundary, we have to expose the key material to the js runtime. From [step 2.1](https://blog.millerti.me/2023/01/22/encrypting-data-in-the-browser-using-webauthn/#step-21-import-the-input-key-material) of the article:
```ts
const inputKeyMaterial = new Uint8Array(auth1ExtensionResults.prf.results.first);

const keyDerivationKey = await crypto.subtle.importKey(
  "raw",
  inputKeyMaterial,
  "HKDF",
  false, // n.b. Extractable flag from here on is false, but the raw key material is in memory already...
  ["deriveKey"],
);
```

For this use case, the prf output is ideally never exposed to the js context.

## Proposal
I propose adding an option to the prf extension that supports returning an non-extractable [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey):
```ts
extensions: {
  prf: {
    eval: {
      first: firstSalt,
      asCryptoKey: true, // New optional field on `AuthenticationExtensionsPRFValues`
    },
  },
},
```

If `asCryptoKey` is set true, then the result object returns a non-extractable `CryptoKey` instead of a `BufferSource`. The resultant key should match the output of the `importKey` call above (i.e. using HKDF).

### Why
To get concrete about why this change can help, we can think about some attack vectors. Note that "key material" below refers to the prf extension results:
- Attacker with js code execution (i.e. from XSS or malicious extension)
  - If an attacker can do code execution in your js context, it's game over already (i.e. attacker could also just exfiltrate data later on, etc). Protecting the key material can limit the blast radius of exposure (i.e. decrypting future/past content offline)
- Buggy code
  - Marking key material unexportable can protect from hidden or obfuscated exposure, e.g. if the developer accidentally logs keys somewhere

## Other related issues / alternatives considered

### Generalized crypto operations
I've seen the [discussion for general crypto operations](https://github.com/w3c/webauthn/issues/1608) which seems relevant but much broader and nebulous. I'm proposing a smaller, concrete change to the prf extension spec.

### The scope of webauthn
I've seen the previous discussion (#1481) on the scope of the webauthn spec and understand the desire to stay out of webcrypto's territory. At the same time, I do also see the [desire to make correct, secure implementations](https://github.com/w3c/webauthn/issues/1595#issuecomment-817217895) easy to do within the spec. It seems like the only way to keep key material out of the js context is to have the webauthn/the prf extension do the webcrypto call behind the scenes.

### Allow arbitrary CryptoKey derivations.
Instead of only allowing HKDF, we could allow passing arbitrary options for use as e.g. RSA or ECDH. This feels out of scope and ties the webauthn api too strongly to the webcrypto one.

Notably, I believe that only outputting HKDF doesn't limit what kinds of constructions folks could implement - they could call `deriveKey` again from this key, etc.

### Don't use prf for this use case
Perhaps this is simply a mismatch in use case, but then what is prf for? I haven't seen much discussion around other use cases. A better fit may be to expose hardware keys in webcrypto and actually decrypt/encrypt bytes on the hardware itself so that keys never leave the HSM, though it's unclear to me if those operations are reasonably performant...

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


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

Received on Monday, 5 June 2023 15:55:43 UTC