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

EIP-0028 ErgoAuth #53

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
178 changes: 178 additions & 0 deletions eip-0028.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# ErgoAuth: user authentication protocol between wallet applications and dApps

* Author: @MrStahlfelge
* Status: Implemented
* Created: 25-Jan-2022
* Last change: 24-Oct-2022
* License: CC0
* Forking: not needed

## Contents
- [Description](#description)
- [Background And Motivation](#background-and-motivation)
- [ErgoAuth authentication protocol](#ergoauth-authentication-protocol)
- [Data Formats](#data-formats)
- [Implementation in Mobile Wallet](#implementation-in-mobile-wallet)
- [Implementation in dApp](#implementation-in-dapp)
- [Benefits for dApps](#benefits-for-dapps)
- [Benefits for Mobile Wallets](#benefits-for-mobile-wallets)

## Description
This EIP defines a standard for trustless authentication of users of a wallet app and an online
dApp.

## Background And Motivation

dApps might want to validate if dApp users are really who they pretend to be. This is especially
useful for dApps that grant certain abilities to holders of special tokens. At the moment,
proving that a user owns a token can only be done by sending the token to a depositary address.
By sending the token, the user proofed to have access privileges to the token.
However, sending token around is not always desirable. Especially for valuable tokens, users might
not want to send it away, and doing two transactions (one to send it to the depositary address, one
to refund it back) costs both time and transaction fees.

Choose a reason for hiding this comment

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

User can just send a token to himself. And there is no need to submit that transaction either, just to check that the transaction is valid via transaction/check endpoint.


To overcome this, ErgoAuth proposes a way to authenticate users trustless to have access to certain
addresses storing a box. The protocol is trustless in both ways: The users don't need to trust
the dApp, because the dApp does not get access to funds or secrets. The dApp don't need to trust
the users or the wallet app, because it can validate the authentication keys.

## ErgoAuth authentication protocol

An authentication with ErgoAuth is driven by a dApp that needs to authenticate a user.

1) The user enters the necessary information in the dApp's UI for the dApp to know if authentication
is necessary. For example, users might enter their P2PK address (or, instead of manually entering,
use ErgoPay to send the address to the dApp automatically).

2) The dApp determines that authenticating the user is needed. For this, the dApp prepares a unique
message that the wallet app should sign with a user's private key, and a SigmaBoolean that the user

Choose a reason for hiding this comment

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

Possible vulnerability. If I understand correctly the link has to be generated on BE. In order to do that Client need to make a request. Here is a scenario:

  1. dApp A (Good dapp) and dApp B (Bad actor) implement the same authentication.
  2. Client want to sign to dApp B.
  3. dApp B makes a request to dApp A to get a unique message.
  4. dApp B ask client to sign that message. and client signs/submits it to dApp B
  5. dApp B submit the signature to dApp A in exchange for an authentication token, now dAppB's dev have an access to Client's dApp A protected routes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not really a vulnerability, depending on context it could be wanted, but it could also be unwanted.
I see following possibility to work against an unwanted case:

  • Wallet application adds the hostname of the replyto endpoint to the signed message. This way, a dApp can check if the message was directly sent or relayed.

Copy link

@capt-nemo429 capt-nemo429 Jun 29, 2022

Choose a reason for hiding this comment

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

Having the hostname added to signedMessage sounds like a really good idea, a timestamp can be also a good addition.

signedMessage pattern suggestions:

We can enforce a pattern that can be simple to parse and verify, for this a semicolon delimited string seems like a good choice, a JSON formatted string is an alternative, a bit verbose one but even simpler to parse.

Proposed fields

  • origin: domain, subdomains and TLD
  • timestamp: Unix timestamp at the moment of the signature. Useful for dApps to guarantee message validity periods.

Semicolon delimited string

  • Pattern: "signingMessage;origin;timestamp;randomBytes"
  • Example "someRandomString;www.sigmavalley.org;1656538465;b955cba40ceb"

JSON formatted string

  • Pattern: "{signingMessage:string,origin:string,timestamp:string,randomBytes:string}"
  • Example: '{"signingMessage":"someRandomString","origin":"www.sigmavalley.org","timestamp":"1656538465","randomBytes":"b955cba40ceb"}'

Copy link
Member Author

Choose a reason for hiding this comment

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

I think semicolon separation is okay here. To make it more random, a prefix could be used:
signingMessageRandomByteso:originRandomBytes
so dApp can check if o:(own host name) is present.

Timestamp does not hurt to have, but in my opinion it is not really necessary since the creation of the signingMessage can be persisted on dApp's side.

Choose a reason for hiding this comment

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

Oh, great! Not sure about the prefix tho. We can make it more random by just increasing the randomBytes length, I'm using an UUID here.

Copy link

@capt-nemo429 capt-nemo429 Jul 5, 2022

Choose a reason for hiding this comment

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

True, my fear is that the ones using it are not aware of the implications

I don't see any problem on having it honestly.

EDIT: But of course that doesn't mean we can't add it. I just see more difficulties here for dApps in finding the timestamp within the signed message, while the hostname is pretty easy to find with a simple contains()

If we use the pattern I suggested above, a dev can check it as simple as signedMessage.split(";")[2]

Choose a reason for hiding this comment

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

@MrStahlfelge @aslesarenko I'm not talking about the man in a middle attack, I'm talking about a much simpler case of dapp A propagating users' requests to server B to get access. Anyone can do this no hacking skills are needed and encryption will not help here. User thinks that he is signing a message of A, while in reality, it's a message of B. Once A has a signed message from the user it can submit it to B, now A have an access to B.

Choose a reason for hiding this comment

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

@MrStahlfelge @capt-nemo429 , what really can solve it is a simple solution. And let me know if this is what you are guys were talking about the whole time. :D

  • The message must include the domain (origin)

  • Wallets (dapp connector) should prohibit from user to sign message if the message.split(':')[0] !== <window.origin> exactly how CORS policies on browser side works (or maybe just set a huge warning like in burn token case?)
    image

  • Server should validate that the message includes the origin.

Without the wallet part, it will not work. If wallets enforces the message standard AppA will not be able to substitute the message with AppB's message. And again if this is what you were talking about all the time, sorry for the misunderstanding!

Choose a reason for hiding this comment

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

But then again @MrStahlfelge why you want the server to generate that message and not the wallet itself?
I think server should be an option and if message is not provided the wallet could generate it using timestamp:origin:randombytes pattern or whatever

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's take discussion to the main PR thread

needs to authenticate for. This might be a P2PK address wrapped in a SigmaBoolean.

3) The dApp presents an ErgoAuth link for the user to click and open the wallet app and a QR code
for mobile users to scan from within the wallet app.

4) The wallet application parses the QR code/link data and obtains a
`ErgoAuthRequestUrl` to fetch the actual `ErgoAuthRequest` data from
(see [Data Formats](#data-formats) section).

5) When `ErgoAuthRequest` is obtained, the wallet presents a screen showing that a dApp wants to
authenticate the user, and the address the request is for. The wallet app should also inform the
user that no funds or moved and no secrets will leave the device.
In a future enhancement, the Auth Request could be relayed to a Cold wallet device. This is an enhancement
of EIP-0019 and would not change ErgoAuth protocol.

6) When the user agrees, the wallet app adds some own bytes to the obtained message from ErgoAuthRequest,
signs it and sends the signed message to the ErgoAuthRequest's replyToUrl. The added bytes include
the host address the authentication request was fetched from, added right after the message defined
by the dApp. This way, dApp can check if a user authenticated via the right domain and there is no
middleman.

8) The dApp validates the signed message. When successful, it can proceed with its flow.

## Data Formats

Wallet apps should be able to initiate ErgoAuth both by using URI schemes
(clickable links) or QR codes.

`ergoauth://<URL>`

An URL is provided without the https prefix. http communication is not allowed except for IP addresses
(in order to test within a local network).

Examples:
* `ergoauth://sigmavalley.io/auth/2021-16b8-66c4-b800-6e52-8ce4` will make the wallet app request
`https://sigmausd.io/auth/2021-16b8-66c4-b800-6e52-8ce4`
* `ergoauth://192.168.0.1/auth` will make the wallet app request
`http://192.168.0.1/auth`

#### Response body: ErgoAuthRequest

The wallet application should request URL and obtain the following data (json format)

```
ErgoAuthRequest:
- signingMessage: String
MrStahlfelge marked this conversation as resolved.
Show resolved Hide resolved
- sigmaBoolean: String (base64 from serialized SigmaBoolean)
- userMessage: String (optional*)
- messageSeverity: String (optional) "INFORMATION", "WARNING"
- replyToUrl: String
```

(Remark: An Ergo p2pk address is a SigmaBoolean, so authenticating a wallet address is possible with this)

The **signingMessage** is a String that is not user-friendly to read in general, as it might contain
information the dApp adds to make it unique. If the signingMessage contains 0-byte character (unicode 0000),
the part of the signingMessage before this sign is interpreted as the user prompt what he is going to sign for
and must be shown to the user.

If provided, the wallet application should show the **userMessage** and display the **messageSeverity**
in a suitable way. It should also show the replyToUrl's hostname so that the user knows to where
the authentication is sent. The replyToUrl's hostname must be the same as the one the request was
fetched from - a wallet application should verify that.

After signing is performed, the
wallet must POST the following data to the dApp using **replyToUrl** from the
request (json format).

```
ErgoAuthResponse:
- signedMessage: String
- proof: String (Base64)
```

`signedMessage`: Message containing the `signingMessage` sent by the dApp with additional bytes added by the
wallet. The addition of random bytes is done to prevent letting the user signing a message that might be used
for unwanted malicious tasks. Besides random based, the signed message must also contain the replyToUrl's
hostname right after the original signing message. This way, the dApp can check if an authentication was done by
the user for that dApp, or if another middleman reuses an authentication.

`proof`: Output of signing `signedMessage`

In case there was an error building the ErgoAuthRequest on the dApp side, the dApp might reply
with an `ErgoAuthRequestError` to inform the user about the error:

```
ErgoAuthRequestError:
- userMessage: String
```

The wallet application will show the user message to the user.

## Cold wallet support
Similar to [EIP-0019](eip-0019.md) for signing transactions from devices not connected to the internet
("cold wallets"), ErgoAuth can be used to sign messages from cold wallets. This is transparent for
dApps and handled by the wallet connected to the internet ("hot wallet").

For this, the `ErgoAuthRequest` must be transferred to the cold wallet via files or QR codes and
the `ErgoAuthResponse` must be transferred back the same way.

The [interchange format to transfer chunks between hot wallet and cold wallet is similar to the one
defined in EIP-0019](eip-0019.md#interchange-format), with name "EARQ" for ErgoAuthRequest and "EARS"
for ErgoAuthResponse. Examples:

{"EARQ":"{\"signingMessage\":\"....\",\"sigmaBoolean\":\"...\",\"userMessage\":\"...\",...}"}

{"EARS":"{\"signedMessage\":\"....\",\"proof\":\"...\"}

The ErgoAuthRequest's `replyToUrl` field must be omitted to save data bandwidth.

Chunking as described in EIP-0019 is supported as well.

## Implementation in wallet app
[Ergo Wallet App #112](https://github.com/ergoplatform/ergo-wallet-app/issues/112)

## Implementation in dApp

[Ergo Appkit #157](https://github.com/ergoplatform/ergo-appkit/pull/157)

dApp:
* [ErgoPay backend example](https://github.com/MrStahlfelge/ergopay-server-example/commit/9271f0ef890d6c8e63789f6c82b65595efe8549a)
* Login to https://www.paideia.im/

## Benefits for dApps
- A dApp or website don’t need to handle user's secrets (mnemonic/private keys), but can
safely validate if a user has access to certain Ergo addresses.
- dApp's users don't need to worry about security of their private keys as the
wallet guarantees they never leave the device. This is especially true if authentication
is done with a cold device.