PassKey Verification canister

What I want is to authenticate users using a passkey(without using Internet Identity). So How can I create a rust canister that will allow users to log in to my application using a passkey and then I can verify it using the verification canister?

Also, I want to know how Intenet Identity implements it.

Hi @Dracian

The Internet Computer supports passkeys / WebAuthn natively (see here). This means you don’t have to do much, simply sign canister calls using the WebAuthn navigator.credentials.get function.

The agent NPM package provided by DFINITY comes with a nice WebAuthnIdentity to do exactly that.

Note: this would then result in the user having to interact with the passkey on every single sign operation. To avoid that, you can create DelegationIdentity that allows signing with a ephemeral key-pair generated in the browser and sign that using a passkey operation, like Internet Identity does it here: internet-identity/src/frontend/src/utils/iiConnection.ts at 09e9544c739bfc2c807d64ac553f5cff1728a329 · dfinity/internet-identity · GitHub
(simply supply your WebAuthnIdentity as the identity parameter).

I hope this helps.

2 Likes

This Helps, But What i need is a way to verify those webauthn signatures on chain, I am assuming this is some sort of RSA encryption so how do I verify it.

If you need to verify generic WebAuthn signatures, you can use this package inside a rust canister. It supports WebAuthn with RS256 and ES256.

Note: Ingress messages are automatically verified by the platform itself. If the goal is to sign ICP transactions with WebAuthn, you do not need to verify the signatures yourself.

So you are telling me I can generate these signatures from a frontend canister using webauthn package. I will have to use the above package inside my backend rust canister to verify those signatures in the backend.
So at the time of creation of the passkey for a particular username of a user, what should I store inside a backend canister? their public private key signature or anything else that I can verify later?

Well, that heavily depends on the use-case. I could give you much better advice if you explained what you are trying to accomplish.

I’m still not entirely convinced that you actually need to verify the signatures yourself (as opposed to the platform).

Here my use case is to use Identity based encryption using VetKeys and to encrypt data on chain,
so for the user t prove his identity we are trying to authenticate him using his passkey and then encrypting the data using that signature.

In that case having the ICP ingress validator check the signatures should be enough. This gives you an authenticated caller for whom you can derive the vetkd_encrypted_key.

The public key that you supply as the encryption_public_key does not need to be tied to a passkey. In fact, it can’t be a public key of a WebAuthn authenticator because it needs to be a BLS public key, which is not supported by the WebAuthn standard (AFAIK).

So in summary:

  1. generate a BLS key pair to use for vetKeys (supply the public key as encryption_public_key)
  2. supply the public key from step 1 to the canister using an authenticated call
    • Sign the call with a WebAuthn identity as outlined in this response.
  3. build the access control mechanism in the canister based on the caller (which is the self-authenticating principal of the WebAuthn public key)

This should remove the need to check signatures in the canister and reduce the amount of code that you need to write.

@franzstefan: Please fact-check the above. :slight_smile:

1 Like

As far as I’m aware VetKeys is not available yet.

Yeah , not on mainnet but beta is available

I didn’t hear about that, I thought only an insecure canister based implementation was available :sweat_smile:

Still looking forward to it becoming available, for now I’m postponing functionality that relies on VetKeys on my end.

But for us most of the functionality relies on vet keys so we have to use the beta one till it’s available on mainnet.

1 Like

@franzstefan: Please fact-check the above.

Thanks, @frederikrothenberger! What you said is exactly right!

  1. generate a BLS key pair to use for vetKeys (supply the public key as encryption_public_key)

For this, the ic-vetkd-utils’ TransportSecretKey can be used.

But if I use the public key for the encryption then won’t it make it vulnerable, like everyone know your public key so they can actually decrypt your data

What @frederikrothenberger meant was that you supply the public key from step 1 as the encryption_public_key to vetkd_encrypted_key. This will return a vetKey that is asymmetrically encrypted under the public key you provided, which means that only you who was the respective private key can decrypt the vetKey and then use it (for example for deriving a symmetric encryption key from it that you then use to encrypt data). The respective key pair is also called “transport” key pair, because it is only used/needed for transporting the vetKey to you (i.e., during “transport”) so that none of the subnet nodes actually learn the vetKey. Note that the transport key can be ephemeral, i.e., you can create a new key pair for every call to vetkd_encrypted_key if you want.