Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Gundb and generate MACI key from wallet signature #615

Closed
wants to merge 60 commits into from

Conversation

yuetloo
Copy link
Collaborator

@yuetloo yuetloo commented Jan 3, 2023

No description provided.

@yuetloo yuetloo marked this pull request as draft January 3, 2023 22:37
@netlify
Copy link

netlify bot commented Jan 17, 2023

Deploy Preview for deterministic-key ready!

Name Link
🔨 Latest commit f4c54c0
🔍 Latest deploy log https://app.netlify.com/sites/deterministic-key/deploys/6411d6bca3bdfe0007a4936f
😎 Deploy Preview https://deploy-preview-615--deterministic-key.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@yuetloo yuetloo linked an issue Mar 16, 2023 that may be closed by this pull request
@netlify
Copy link

netlify bot commented Mar 21, 2023

Deploy Preview for clrfund-testnet ready!

Name Link
🔨 Latest commit ac4fd16
🔍 Latest deploy log https://app.netlify.com/sites/clrfund-testnet/deploys/642481322b13ad000875510c
😎 Deploy Preview https://deploy-preview-615--clrfund-testnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@yuetloo
Copy link
Collaborator Author

yuetloo commented Mar 23, 2023

@daodesigner , I have reimplemented the MACI key generation using the public key from the passkey and would like to hear your opinion about this approach.

According to the webAuthn standards, 3 components are involved:

  1. Web application
  2. Backend Server (with a database storing public keys for authentication)
  3. Authenticator (that generates and stores the passkey)

Because Clrfund runs on the client side only (no backend server), we will not store the public key, instead, we will use ecrecover to recover the public key for generating the MACI key. Because of this, we currently only support the ECDSA p256 curve, which allows public key recovery from the signatures.

Note that only the website that owns the domain can prompt the users to sign a random message with the passkey they created on the website. So, only the website can get the signature to recover the public key.

Users can manage (rename/delete) the passkeys they created on the authenticator. Here's how to manage passkeys:
Google passkey support
Sign in with passkey on Apple

Security wise, I think it's more secure in that users do not need to sign a static message with their wallets, and no need to remember passwords.

However, there are limitations:

  1. can only use authenticators that support p256 curve, which should be mostly supported as this is one of defaults curve
  2. by default, chrome stores the passkey on the chrome profile (tested on MacOS only), so, users will only be able to access their votes from the same browser. Apple users can select to store the passkey on the keychain by selecting "other device" when creating the passkey. This will allow vote access from iPhone if the keychain is synced on iCloud.

Here's a test site if you would like to see how the UX flow:
https://clrfund-testnet.netlify.app/#/

How to get the test DAI used by the app:
https://github.com/clrfund/monorepo/blob/deterministic-key/docs/testing.md

@daodesigner
Copy link
Collaborator

daodesigner commented Mar 23, 2023

Very cool, definitely looks way better off the bat, (though ideally you could use the private key) and will look through the changes/try it out tonight 😊. Great work!

@yuetloo
Copy link
Collaborator Author

yuetloo commented Mar 23, 2023

Very cool, definitely looks way better off the bat, (though ideally you could use the private key) and will look through the changes/try it out tonight 😊. Great work!

Thank you, looking forward to your feedback

Copy link
Collaborator

@daodesigner daodesigner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any chance there's a diagram of all the interactions happening?

if (key) {
return key
} else {
const key = await credential.create()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the public key or the private key?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the public key of the passkey, we can't get the private key from the passkey

}

const credential = new Credential(address)
const keys = await credential.get()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this get locked down? this just on the user's browser?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is between the user's browser and the authenticator.

timeout: TIMEOUT,
}

const credential = await navigator.credentials.get({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does navigator come from?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

navigator is the user's browser api

publicKey,
})

return recoverPubKeyFromCredential(credential)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just return one of the private keys instead, which would be a better source of entropy

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The private key is never exposed, what we get from the authenticator is only the signature of the data we asked it to sign. That's why we are recovering the public keys from the signature.

}

async create(): Promise<string> {
const challenge = utils.randomBytes(32)
Copy link
Collaborator

@daodesigner daodesigner Mar 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user should save this somewhere, I assume this is the password?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is not a password, this is just a random piece of data that we ask the authenticator to sign. The standard suggests using random data to avoid replay attack, but, in our case, we never expose any data from the passkey other than using the public key to encrypt the votes on the browser, we could probably use a constant here.

Obviously, this logic won't work if we ever exposed the public key or can trick the authenticator and user for the signature. I've tried testing from different domains and passing different domain from the api, all tests didn't get the signature.

@yuetloo
Copy link
Collaborator Author

yuetloo commented Mar 29, 2023

any chance there's a diagram of all the interactions happening?

Yes, added https://github.com/clrfund/monorepo/blob/deterministic-key/docs/passkey.mmd

@yuetloo
Copy link
Collaborator Author

yuetloo commented Jun 6, 2023

changes merged

@yuetloo yuetloo closed this Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replace GunDB with something more web3 native
3 participants