Tim Daubenschütz
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee
  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Multisigned Multisigned is an open source digital locker for Ethereum seed phrases. ## Motivation The self custodial Ethereum wallets' user experience is bad and hasn't notably improved over the last few years. While this is not exactly a problem for crypto currency applications, for decentralized social media apps this is a death sentence as our web2 counter parts are alive and innovating players who keep shipping the best in class user experiences. For essentially all of crypto-related decentralized social media, over the course of the last 2-3 years, this has meant TAM-constraint growth and zero sum customer acquisition. The fact of the matter is: Crypto-based decentralized social media cannot grow with the Ethereum self custody wallets as a substrate. That is not even a failure of Ethereum wallet providers. The fault rather lies in the fact that building crypto-based decentralized social media and nation-state-secure crypto currency have different goals. Hence, we think it is urgently necessary for crypto-based decentralized social media to branch out into its own wallet vertical and to solve their problems with bespoke solutions. In the document below we outline a concept to client-side-encrypt and store Ethereum seed phrases on open source servers to immensely improve the UX of crypto-based decentralized social media applications. ## User Experience There are a few things to say about the user experience of using passkeys in a crypto-based decentralized social media application versus using a self custodial Ethereum wallet. For the sake of this document, it is simply important to note that, for users, interacting with Passkeys doesn't appear as a risk to their funds. While there are a few sites that allow people to transact funds with Passkeys, generally we trust that users are intuitively aware of the domain separation Passkeys implement and hence do not associate a Passkey dialogue with a possibility of endangering their savings. Passkeys obviously also come pre-installed on all major operating systems now. The PRF extension is now [widely available](https://www.passkeyprf.com/). Meanwhile crypto currency wallets will continue to try to lock down users private keys and seed phrases ## Safety Considerations and Limitations It's important to highlight that we intend this protocol to be used for crypto-based decentralized social media applications. These usually allow users, for example, to delete prior statements. The potential to do irrevocable damage to a crypto currency account is obviously very high and so wallet providers do their utter most to lock down seed phrases. In our view, this potential to do irrevocable damage is much lower for crypto-based decentralized social media. And in cases where the stakes are high, dapps can always choose to allow users to use self-custodial Ethereum wallets. ## Protocol ### Registration 1. The protocol uses envelope encryption. 2. When the user opens a website, sees and clicks a "Sign up" button, an operating-system-specific Passkey dialog opens asking the user to create a Passkey for the website. The user confirms. 3. With this dialogue, the user is registering a public private key pair with the multisigned server. In return for signing up they receive a 15 minute valid JWT session token. A salted PRF value is generated from the credential creation dialogue too. It's used as an input to generate an AES encryption key (KEK, key encryption key). 4. The user's browser now generates another AES encryption key that we call data encryption key (DEK). The seed value of this DEK is entirely random. 5. An Ethereum seed phrase is generated and wrapped with the DEK. We call this the vault. 6. The DEK itself is wrapped with the Passkey-PRF-derived KEK. 7. Using the JWT session token from step 3, the user's browser now sends the wrapped DEK and the vault to the server. 8. The server stores the vault in one table and the DEK in reference to the vault ID. 9. The Ethereum seed phrase is stored for the time of the browser or app's session in the user browser's local storage. Upon closing the browser tab or app, the Ethereum seed phrase is deleted from the device. ```mermaid sequenceDiagram actor User participant Browser participant OS as OS/Passkey Provider participant Server User->>Browser: Click "Sign up" Browser->>Server: POST /register/begin Note over Server: Generate lockboxId (UUID)<br/>Generate registration challenge Server->>Browser: Return registration options<br/>(challenge, rpID, rpName, etc) Browser->>OS: navigator.credentials.create()<br/>with PRF extension OS->>User: Show Passkey dialog User->>OS: Confirm creation Note over OS: Generate public/private keypair<br/>Generate PRF salt output OS->>Browser: Return credential response<br/>(credentialId, publicKey, PRF output) Note over Browser: Derive KEK from PRF output Note over Browser: Generate random DEK Note over Browser: Generate Ethereum seed phrase Note over Browser: Encrypt seed phrase with DEK<br/>(creates vault) Note over Browser: Encrypt DEK with KEK<br/>(creates wrapped DEK) Browser->>Server: POST /register/complete<br/>(credential response) Note over Server: Verify registration response<br/>Create lockbox with lockboxId<br/>Store passkey credential Server->>Browser: Return JWT token (15min) Browser->>Server: PUT /lockbox<br/>(wrapped DEK + vault, with JWT) Note over Server: Store encryptedData in lockbox Server->>Browser: 200 OK Note over Browser: Store plaintext seed phrase<br/>in session storage<br/>(deleted on close) ``` ### Login 1. When the user opens a website and is prompted to sign a login challenge with their Passkey. In return for completing the challenge, the server sends back a 15 minute valid JWT session token. A salted PRF value is also generated during the Passkey challenge dialogue. It's used as an input to generate an AES encryption key (KEK, key encryption key). 2. Using the JWT session token, the user now requests to access the wrapped DEK and the vault. The server sends both. 3. The user's browser unwraps the DEK, the unwrapped DEK is then used to unwrap the vault. The DEK is discarded. 4. The vault's Ethereum seed phrase is stored for the time of the browser or app's session in the user browser's local storage. Upon closing the browser tab or app, the Ethereum seed phrase is deleted from the device. ```mermaid sequenceDiagram actor User participant Browser participant OS as OS/Passkey Provider participant Server User->>Browser: Open website, click "Login" Browser->>Server: POST /login/begin Note over Server: Generate authentication challenge Server->>Browser: Return authentication options<br/>(challenge, rpID, etc) Browser->>OS: navigator.credentials.get()<br/>with PRF extension OS->>User: Show Passkey dialog User->>OS: Confirm authentication Note over OS: Sign challenge with private key<br/>Generate PRF salt output OS->>Browser: Return assertion response<br/>(credentialId, signature, PRF output) Browser->>Server: POST /login/complete<br/>(assertion response) Note over Server: Verify signature<br/>Lookup passkey by credentialId<br/>Update counter Server->>Browser: Return JWT token (15min) Note over Browser: Derive KEK from PRF output Browser->>Server: GET /lockbox (with JWT) Note over Server: Lookup lockbox by lockboxId from JWT Server->>Browser: Return encryptedData<br/>(wrapped DEK + vault) Note over Browser: Unwrap DEK with KEK Note over Browser: Unwrap vault with DEK Note over Browser: Discard plaintext DEK Note over Browser: Store plaintext seed phrase<br/>in session storage<br/>(deleted on close) ``` ### Adding a second Passkey A user's identity should not permanently bind to a Passkey as they are not easily portable, e.g., across ecosystems. The user now has two devices: phone1 and phone2. Assume the user has successfully registered and is logged into the app with phone1. Both devices are of different Passkey ecosystems (e.g. Apple and Google). The user now wants to add phone2 as a second device. 1. On phone2, the user navigates to the app and clicks "Add recovery option." The user is presented with a Passkey registration dialogue. Upon completing the registration challenge, phone2 receives a 15 minute valid JWT session token. A PRF value is generated from the Passkey dialogue too and is used as an input to generate an AES encryption key (KEK). 2. An ephemeral ECDH keypair (public + private) is generated on phone2. The ECDH's public key, along with the signing key of phone's Passkey credential are shown as a QR code along with the message "Scan this QR code with phone1" 3. On phone1 (already logged in), the user scans the QR code in the app and deserializes phone2's ECDH public key and its Passkey credential's public key. 4. To get the DEK, phone1 now repeats the Login proceedure but doesn't discard the plaintext DEK in step 3. 5. Phone1 wraps the plaintext DEK using phone2's public ECDH key and sends it along with phone2's Passkey credential public key to an ephemeral storage slot on the server. 6. The server now allows phone2 to store a new DEK in phone1's DEK and vault storage. 6. Using the JWT session key from step 1, phone2 polls the server's ephemeral storage slot, finds the encrypted DEK and downloads it. 7. Phone2 also requests and downloads phone1's encrypted vault. 7. Phone2 unwraps the DEK using the ECDH private key and discards the ECDH keypair. 8. Phone2 now wraps the plaintext DEK with its own KEK. 9. Phone2 unwraps the vault and stores the Ethereum seed phrase in the app's session storage. 9. Phone2 sends the newly wrapped DEK to the server where it is stored along side phone1's vault and DEK. ```mermaid sequenceDiagram actor User participant Phone2 as Phone2 Browser participant OS2 as Phone2 OS/Passkey participant Server participant Phone1 as Phone1 Browser (logged in) participant OS1 as Phone1 OS/Passkey Note over Phone1: Phone1 is already logged in User->>Phone2: Click "Add recovery option" Phone2->>Server: POST /register/begin Server->>Phone2: Return registration options Phone2->>OS2: navigator.credentials.create()<br/>with PRF extension OS2->>User: Show Passkey dialog User->>OS2: Confirm creation Note over OS2: Generate keypair<br/>Generate PRF output OS2->>Phone2: Return credential response Phone2->>Server: POST /register/complete Server->>Phone2: Return JWT token (15min) Note over Phone2: Derive KEK2 from PRF output<br/>Generate ephemeral ECDH keypair Phone2->>Phone2: Display QR code<br/>(ECDH public key + credential public key) User->>Phone1: Scan QR code with Phone1 Phone1->>Phone1: Parse ECDH public key<br/>and credential public key Note over Phone1: Repeat login procedure to get DEK Phone1->>Server: POST /login/begin Server->>Phone1: Return authentication options Phone1->>OS1: navigator.credentials.get()<br/>with PRF extension OS1->>User: Show Passkey dialog User->>OS1: Confirm authentication OS1->>Phone1: Return assertion + PRF output Phone1->>Server: POST /login/complete Server->>Phone1: Return JWT token Note over Phone1: Derive KEK1 from PRF output Phone1->>Server: GET /lockbox (with JWT) Server->>Phone1: Return encryptedData Note over Phone1: Unwrap DEK with KEK1<br/>Keep plaintext DEK in memory<br/>(don't discard yet) Note over Phone1: Encrypt DEK with Phone2's ECDH public key Phone1->>Server: POST /recovery/transfer<br/>(encrypted DEK, Phone2 credential pubkey, with JWT) Note over Server: Store in ephemeral slot<br/>Authorize Phone2 credential<br/>to access lockbox Server->>Phone1: 200 OK Note over Phone1: Now discard plaintext DEK Phone2->>Server: GET /recovery/transfer (with JWT, polling) Server->>Phone2: Return ECDH-encrypted DEK Phone2->>Server: GET /lockbox (with JWT) Server->>Phone2: Return vault Note over Phone2: Decrypt DEK with ECDH private key<br/>Discard ECDH keypair Note over Phone2: Wrap DEK with KEK2 Phone2->>Server: PUT /lockbox/add-key<br/>(wrapped DEK2, with JWT) Note over Server: Store Phone2's wrapped DEK<br/>alongside existing data Server->>Phone2: 200 OK Note over Phone2: Unwrap vault with DEK<br/>Store seed phrase in session storage ``` #### Considerations It is important that phone1 learns about phone2's decryption key through a QR code as this then guarantees that the server never gets a chance to steal or see the Ethereum seed phrase. ### Rotating the DEK [TODO] ## Server Architecture As the encrypted blob storage is for many users the only way to access their application credentials, then it is important to write a few words about how we intend to design the server architecture. We think that decentralized social mediabuilders have a shared interest in keeping users' identities safe, so we think that them running their own instances of these encrypted blob servers makes sense. We can also envision dapp-independent providers of this blob storage to emerge. Which is fine, but also not something we intend to design for in this first iteration. ## Feasibility To understand the feasibility to implement out above outlined scheme we have to understand how widely supported Passkeys with the PRF extension are. This is not exactly an easy task as there are many nuances to consider. Generally speaking, we believe that Passkeys and PRF will find its way into most operating systems and browsers. So we are optimistic that our scheme will be widely supported over the long term. However, we think it's also important to thoroughly validate some potential pitfalls. A few examples of Passkeys+PRF support being sub-optimal are: 1. Passkeys and PRF are be unavailable on a logged out version of Chrome. 2. Authenticators like iCloud Keychain are shipped along with operating system upgrades, and so while browsers may already support Passkeys and PRF, hence suggesting a large addressable market, an outdated authenticator can still fail the process, shrinking the addressable market. ## Call to Action We have written this document for it to be circulated among potential stakeholders and investors. We think such a system could be built now pretty easily, the technology is ready and it seems no one has built it yet. But we'd like to work on this as a community of interested parties to make this safe, co-owned and successful. If such a system already exists, we don't want to build it, but reuse and adjust the existing system. We are most likely not interested in just using Ethereum smart accounts or newly developed Ethereum crypto currency wallets as they tend to favor moving user funds around which is a misaligned goal to what we intend to use Ethereum keys for. This, however, is by no means meant to exclude the Ethereum wallet community of which we consider ourselves a part too. If you would like to help drive this project forward, please reach out to @freeatnet or @timdaub on Telegram. Please also join our Telegram group: https://t.me/+MT5G5BZUQTU2ODRk ## Conclusion These days it almost feels as if it was over for crypto-based decentralized social media. Not even Ethereum leadership seems to embrace its vision anymore. This can be a curse if we assume their help to prosper. Seeking a split in how we design wallets for decentralized social media, we'd like to argue, can maybe also be a blessing. We can finally pave our own path, solve our own problems and we may still benefit from the excellent research pipeline that crypto currencies has generated.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully