Do keys allow user identification?

I am the proud (?) owner of Nitrokey 3.

During the registration process on Github I came across the following text:

"If your security key is capable of verifying your identity (for example, Touch ID, Windows Hello, Android thumbprints, or PIN-locked or biometric hardware keys), then it’s eligible to be upgraded. The next time you sign in with that security key, we’ll ask you if you want to upgrade it to a passkey, which will re-register it with your passkey provider. This re-registration ensures that your passkey is discoverable during authentication, and synced, if supported. Because passkeys are privacy preserving, you might have to trigger your passkey a few times during that upgrade flow so we can make sure we’re upgrading the right credential Once you do, you’re all set. “for a passwordless experience.”

I’m curious about the phrase “which will re-register it with your passkey provider”? Does this mean Nitrokey collects my data (name, address, bank account number) and compares it with all the physical keys I’ve purchased? Does Nitrokey provide this information to identify me on websites?

Let’s say I’m registered on website 1 under the username “Tom Smith.” On website 2, I’m registered under the username “Ldskgjpd.” I used the same Nitrokey for registration, but different email addresses.

Situation 1) All data from both websites has been leaked. Is it possible to determine if “Tom Smith” and “Ldskgjpd” are the same person?

Situation 2) The same person works as an administrator for two companies and has full access to all data on both websites, including technical information. Can he find out that “Tom Smith” and “Ldskgjpd” are the same person?

I’ll try to answer this myself, since I think I’ve figured it out a bit.

When logging in to a website, a request is made to generate a temporary password corresponding to the unique ID of the physical key. During this request, the website sends a site-identifying string of characters (for example, “github.com”), the value of which is mixed with the unique device ID when generating the temporary password. This way, different temporary passwords are generated for each website, and the administrator of both websites won’t be able to tell that the same user is registered on different websites (unless, of course, the websites send different site-identifying strings of characters).

BUT!!! This works only if the website doesn’t request a key certificate during registration. This certificate uniquely identifies the physical key and allows for user identification!!!

Not all websites request a certificate, and this can be verified. ChatGPT wrote a script for me that allows you to determine whether a website requests a certificate or not. Based on my superficial knowledge of JavaScript, the script is safe, does no harm, and doesn’t send anything anywhere.

Using the script is quite unusual for me: create a bookmark in the browser with any name and paste the script code under the URL. When registering a new key, open the console (F12) and click on the bookmark (having a new key isn’t required). Then click “Add a new key” and check the console to see whether the site has requested a certificate.

Script:

javascript:(function(){  function color(text,color){console.log("%c”+text,"color:”+color+";font-weight:bold”);}  function installInterceptor(){    if(navigator.credentials.\__patchedWebAuthn) return;    const origCreate=navigator.credentials.create;    const origGet=navigator.credentials.get;    navigator.credentials.create=async function(opts){      console.group("🆕 WebAuthn REGISTER (navigator.credentials.create)”);      if(opts && opts.publicKey){        const pk=opts.publicKey;        color("RP ID: "+(pk.rp?.id || "(not specified)”),"dodgerblue”);        const att=pk.attestation ?? "(not specified — means none by default)”;        if(att==="direct”) color("⚠%EF%B8%8F attestation: "+att+” (requests a device certificate!)”,"red”);        else if(att==="none”) color("✅ attestation: "+att+” (private, no certificate)”,"green”);        else color("ℹ%EF%B8%8F attestation: "+att,"orange”);        if(pk.user) console.log("user.id:”, pk.user.id ? new TextDecoder().decode(pk.user.id) : "(binary id)”);        if(pk.challenge) console.log("challenge:”, pk.challenge);      } else {        color("⚠%EF%B8%8F Called without publicKey”,"gray”);      }      console.groupEnd();      return origCreate.apply(this,arguments);    };    navigator.credentials.get=async function(opts){      console.group(”%F0%9F%94%90 WebAuthn LOGIN (navigator.credentials.get)”);      if(opts && opts.publicKey){        const pk=opts.publicKey;        color("RP ID: "+(pk.rpId || "(not specified)”),"dodgerblue”);        if(pk.allowCredentials?.length) console.log("allowCredentials:”, pk.allowCredentials);        color("ℹ%EF%B8%8F Certification is not used during login (only during registration)”,"gray”);      } else {        color("⚠%EF%B8%8F Called without publicKey”,"gray”);      }      console.groupEnd();      return origGet.apply(this,arguments);    };    navigator.credentials.\__patchedWebAuthn=true;    color("✅ WebAuthn Inspector is active — open console (F12 → Console)”,"green”);  }  installInterceptor();  setInterval(installInterceptor,2000);})();

What should I do if a site requests a certificate? Some browsers can intercept the key certificate and modify it (Firefox), but this isn’t certain, and I’ll ask for public opinion in another thread.

AFAIK privacy is deeply embedded into the FIDO2 protocol. The attestation certificate that is being requested is (dependent on the manufacturer) the same for a batch or a complete series of keys. While some fingerprinting is possible, it is only known that someone is using a hardware from Nitrokey. This is used e.g. to identify the FIDO2 certification level to determine whether there is a secure element storing the FIDO2 credentials.

2 Likes

I also thought that keys were purchased to ensure user privacy. I became curious and tried to study how it worked. I checked with the LLM and then checked other sources. And it seemed to confirm that websites can request device identification from a crypto token.

The LLM even wrote a JavaScript script for me, which I posted on this forum to determine whether a web page requests token identification data. It turns out that Google does, but GitHub doesn’t.

When I tried to register a token with Google, Firefox warned me that Google was requesting additional information that could affect my privacy. I believe this is the token’s unique data. Firefox even offered me the option to refuse to provide this data, send random data, or send it as is.

The implementation of the token data provisioning mechanism may vary depending on the manufacturer’s policies. Some may send (as you say) data unique to an entire batch of keys (which I consider a major problem for codification if the batches are small), data unique to the manufacturer, or data unique to the key.

I tried to find this information in the Nitrokey specifications, but I couldn’t find it. I haven’t received any responses on the forum either, so I think they’re sending unique key data.

@sosthene-nitrokey Do you have any details to share how Nitrokey 3 handles the FIDO 2 product identification? Are the batch sizes large enough so that it stays quite anonymous? Is there always a user confirmation step before IDs are shared with the relaying party?

An AAGUID is shared with the website, but this ID only identifies the type of device you have. This ID is the same for all Niitrokeys sold for a given model, so it can’t be used to identify you, beyond the fact that you’re using a specific Nitrokey model.

Device AAGUID
Nitrokey 3 xN (lpc55) ec99db19-cd1f-4c06-a2a9-940f17a6a30b
Nitrokey 3 AM (nrf52) 2cd2f727-f6ca-44da-8f48-5c2e5da000a2
Nitrokey Passkey 9a03e537-4cbe-4a01-b2e2-242e0dd9a59b