Skip to content

njofce/webauthn-wallet

Repository files navigation

ERC4337 Accounts with on-chain sec256r1 signature verificaiton

This repository contains the primitives for defining and deploying ERC-4337 accounts using on-chain sec256r1 signature verification. It uses a gas-efficient approach as proposed in (FCL)[https://github.com/rdubois-crypto/FreshCryptoLib].

We are providing the following primitives:

  1. A Precomputations server written in Rust
  2. A WebAuthnAccount ERC4337 smart contract that validates a sec256r1 signature on-chain as part of the userOp validation phase.
  3. A WebAuthnAccountFactory smart contract that deterministally deploys an account contract for a given credential and public key.
  4. An abstract MFAAccount smart contract which can be used in creating and deploying MFA accounts, where the second signature authentication factor is a sec256r1 signature.
  5. A simple Typescript SDK for interacting with WebAuthn standard on the client side and generating all the necessary
  6. An example app written in Next.js that deploys a WebAuthnAccount on-chain and simulates a userOp with a valid signature.

Running the example application

  1. Start the Precomputations server

It is recommended to run the precomputations server using Docker to avoid installing SageMath locally in your machine. Follow the Readme in the precomputations_server to understand more about how it works and how to run it.

  1. Build the local SDK
    cd sdk/
    pnpm i
    pnpm run build-all
  1. Run the example application (make sure to populate the .env file with the right parameters)
    cd examples/webauthn-account
    npm run dev

The example will allow you to create a webauthn wallet, which will deploy the wallet on-chain, and sign a transaction with that wallet.

You can use the code in the example in your apps to deploy WebAuthnAccount wallets as well as sign transactions using those.

WebAuthn add-on

This repo contains an add-on that can be used for an MFA or multi-sign smart contract account, which provides the foundation for validating sec256r1 signature on-chain using a precomputed table for public key parameters, as well as managing the devices. However, any project can provide a different implementation for this and doesn't necessarily need to follow any guidelines, but only use the provided primitives from this repo as a starting point.

This smart contract provides the primitives for:

  1. Adding a device as a second factor for validating a sec256r1 signature on-chain. This will deploy the precomputations bytecode on chain, and store the address.
  2. Validating a signature from that device.
  3. Removing the device, only by providing a valid signature.

Potential Risks

The full fledged development of the WebAuthnAccount is still in progress, and as you might have noticed so far, there are a few certain risks with the current state of the account:

Passkey Recovery

The most simple explanation is that losing your passkey would mean losing your funds.

Even though it’s fairly simple to store your passkey across multiple devices (ex. using iCloud), losing it is still an option, and we wouldn’t want you to lose control over your funds if that scenario was to happen. An on-chain direct recovery option would simply mean a replacement of the current passkey with a new passkey, which would be a rather complicated and expensive task to perform, but worth it nevertheless.

The (cheaper) option we are currently exploring is one which includes a recovery private key, which then can be used to replace a passkey (whether it’s lost or not). Even though the entire point of the WebAuthnAccounts is to avoid using private keys, we need a computationally cheap alternative for on-chain second factor for account recovery. The process will require several steps:

  1. You will first need to register a public key (of an ECDSA key pair) in the WebAuthnAccount as a passkey recovery method.
  2. You can generate a new WebAuthn attestation with a new passkey.
  3. A valid ECDSA signature is generated using the new public key coordinates, ex. ecdsa(keccak256(new_pub_key_x, new_pub_key_y))
  4. New precomputations code is generated by interacting with the precomputations server for the newly created public key
  5. A specific “Reset” or “Recovery” UserOp is triggered that will deploy the new precomputations table and update the credential and public key in the WebAuthnAccount only if a valid ECDSA signature is provided for the new public key coordinates using the previously registered ECDSA public key.

Trusted Precomputation Server

Generating a ready to be deployed precomputations bytecode is a compute-heavy task that needs to be done on a server. That introduces a centralisation step and a potential vulnerability that, in the current state, could lead to loss of funds if the account is deployed using a non-trusted precomputation server.

One scenario that can happen is for a given x & y coordinates of a public key, the server would return a precomputation bytecode for different credential, which could be in a possession of a hacker. The user ends up deploying the account with their public key but with a different precomputation bytecode. This can lead to the user sending invalid signatures to the smart contract, however the hacker is not able to send valid signatures and pull any funds as well, since the signature verification step is linked to the user’s public key.

This issue can certainly lead to a bad outcome if account is not generated using a trusted precomputations server.

One way to mitigate this would be a local validation/simulation of the generated precomputation bytecode, which would require the user an extra interaction with their authenticator device and impact the overall UX. We will be exploring this and other approaches to remove the need to always rely on a trusted precomputation server, while maintaining a smooth UX.