]> git.ardis.lu - static-encrypt-passkey/commitdiff
Add Yubikey support (resident keys only)
authorArdis Lu <ardisglu@gmail.com>
Thu, 31 Jul 2025 07:33:35 +0000 (00:33 -0700)
committerArdis Lu <ardisglu@gmail.com>
Thu, 31 Jul 2025 07:33:35 +0000 (00:33 -0700)
In the future, non-resident keys may be allowed by storing the credential `rawId` in `localStorage`.

script.js

index b2cc09ed86a3bd29e8a37ee5e1b338e26caa0fb9..8a9a40d7aa598ab61e68d6d19b3fd6b9266e5da6 100644 (file)
--- a/script.js
+++ b/script.js
@@ -1,5 +1,8 @@
 /**
- * Create a new passkey and get a pseudo-random value produced from the passkey PRF extension.
+ * Create a new resident passkey and get a pseudo-random value produced from the passkey PRF extension.
+ * 
+ * If the passkey does not return a PRF value after creation, try to sign in immediately after passkey creation
+ * and get the PRF value from the sign in flow.
  * @see {@link https://www.w3.org/TR/webauthn-3/#prf-extension}
  * @returns {Promise<ArrayBuffer&{byteLength:32}>} A 32 byte long `ArrayBuffer` containing a pseudo-random
  * value produced from the passkey PRF extension.
@@ -8,12 +11,13 @@ async function createPrf() {
   return navigator.credentials.create({
     publicKey: {
       rp: { name: '' },
-      user: { id: new ArrayBuffer(1), name: '', displayName: '' },
+      user: { id: new ArrayBuffer(1), name: crypto.randomUUID(), displayName: '' }, // Windows Hello requires non-empty id; Yubikey requires non-empty name
       pubKeyCredParams: [{ type: 'public-key', alg: -8 }, { type: 'public-key', alg: -7 }, { type: 'public-key', alg: -257 }],
       extensions: { prf: { eval: { first: new ArrayBuffer(0) } } },
-      challenge: new ArrayBuffer(0)
+      challenge: new ArrayBuffer(0),
+      authenticatorSelection: { residentKey: 'required' } // Resident key is required because there is no server to store c.rawId
     }
-  }).then(c => c.getClientExtensionResults().prf.results.first);
+  }).then(c => c.getClientExtensionResults().prf?.results?.first ?? getPrf()); // Yubikey does not return PRF on creation, workaround is sign in immediately after creation
 }
 
 /**
@@ -91,6 +95,7 @@ export async function encrypt(plaintext) {
   const info = encoder.encode('https://github.com/ardislu/static-encrypt-passkey');
 
   const prf = await createPrf();
+
   const key = await getKey(prf, salt, info);
   const ciphertext = new Uint8Array(await crypto.subtle.encrypt(
     { name: 'AES-GCM', iv },
This page took 0.033248 seconds and 4 git commands to generate.