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

sapphire: initial notes on authenticated calls & contract authentication #476

Merged
merged 30 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8cc1219
sapphire: initial notes on authenticated calls & contract authentication
CedarMist Jul 25, 2023
8e75004
sapphire: improved authentication verbiage
CedarMist Jul 25, 2023
ec319e8
sapphire: added hyperlink to sapphire-paratime NPM package.
CedarMist Jul 25, 2023
8262f9b
Merge branch 'main' into CedarMist/712-auth
CedarMist Jul 25, 2023
42f6187
sapphire/authentication: added EIP-712 link and side-menu item
CedarMist Jul 28, 2023
583057b
sapphire/authentication: feedback from aefhm
CedarMist Jul 31, 2023
d13bae5
sapphire/authentication: suggestions from Matevz
CedarMist Aug 2, 2023
66623ed
sapphire/authentication: changed order of paragraphs
CedarMist Aug 4, 2023
8f18d71
sapphire/authentication: add notes about cached signed queries
CedarMist Aug 7, 2023
f84db5e
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 9, 2023
191172b
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
25bee5c
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
4161625
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
bcd2ea3
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
365c51f
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
080447e
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
f6fc788
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
170ea89
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
8700eb5
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
78381b1
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
2fd74ab
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
e83da70
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
49e898a
Update docs/dapp/sapphire/authentication.md
CedarMist Aug 16, 2023
d5c5f9a
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
d67b20e
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
1e73595
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
d2d6591
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
dba0d02
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
9158558
Update docs/dapp/sapphire/authentication.md
CedarMist Oct 10, 2023
e9a8410
Merge branch 'main' into CedarMist/712-auth
CedarMist Oct 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions docs/dapp/sapphire/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
description: Authenticate users with your confidential contracts
---

# View-Call Authentication

User impersonation on Ethereum and other 'Transparent EVMs' isn't a problem because everybody can see all data, however with the Sapphire confidential EVM it is necessary to prevent contracts from revealing confidential information to the wrong person - for this reason we cannot allow arbitrary impersonation of any `msg.sender`.
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

There are four types of contract calls:
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

1. Contract to Contract calls
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
2. Unauthenticted view calls (Queries)
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
3. Authenticated view calls (Signed Queries)
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
4. Transactions (authenticated by signature)

By default all `eth_call` queries used to invoke contract functions have the `msg.sender` parameter is set to `address(0x0)`. And when a transaction is submitted it is signed by a keypair (thus costs gas and can make state updates) the `msg.sender` will be set to the signing account.

Intra-contract calls always set `msg.sender` appropriately, if a contract calls another contract in a way which could reveal sensitive information, the calling contract must implement access control or authentication.
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

## Sapphire Wrapper

The Ethereum provider wrapper provided by the [@oasisprotocol/sapphire-paratime](https://www.npmjs.com/package/@oasisprotocol/sapphire-paratime) `sapphire.wrap` function will automatically end-to-end encrypt calldata when interacting with contracts on Sapphire, this is an easy way to ensure the calldata of your dApp transactions remain confidential - although the `from`, `to`, and `gasprice` parameters are not encrypted.
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

However, once the Sapphire wrapper is requested to sign a transaction (thus attaching the provider to a signer, or converting it into one) then subsequent `view` calls via `eth_call` will be automatically signed, these are called 'Signed Queries' meaning `msg.sender` will be set to the signing account. This may not be an ideal user experience and can result in frequent pop-ups requesting they sign queries which wouldn't normally require any interaction on Transparent EVMs.
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

```solidity
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
contract Example {
address owner;
constructor () {
owner = msg.sender;
}
function isOwner () public view returns (bool) {
return msg.sender == owner;
}
}
```

In the sample above, assuming we're calling from the same contract or account which created the contract, calling `isOwner` will return:

* `false`, for `eth_call`
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
* `false`, with `sapphire.wrap` but without an attached signer
* `true`, with `sapphire.wrap` and an attached signer
* `true`, if called via the contract which created it

CedarMist marked this conversation as resolved.
Show resolved Hide resolved
## Sign-in with EIP-712

One strategy which can be used to reduce the number of transaction signing prompts when a user interacts with contracts via a dApp is to use [EIP-712](https://eips.ethereum.org/EIPS/eip-712) to 'Sign-in' once per day (or per-session), in combination with using two wrapped providers:
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

1. Provider to perform encrypted but unauthenticated view calls
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
2. Another provider to perform encrypted and authenticated transactions (or view calls)
- The user will be prompted to sign each action.

The two-provider pattern, in conjunction with a daily EIP-712 sign-in prompt ensures all transactions are end-to-end encrypted and the contract can authenticate users in view calls without frequent annoying popups.
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

The code sample below uses an `authenticated` modifier to verify the sign-in
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

struct SignatureRSV {
bytes32 r;
bytes32 s;
uint256 v;
}

contract SignInExample {
bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
string public constant SIGNIN_TYPE = "SignIn(address user, uint32 time)";
bytes32 public constant SIGNIN_TYPEHASH = keccak256(bytes(SIGNIN_TYPE));
bytes32 public immutable DOMAIN_SEPARATOR;

constructor () {
DOMAIN_SEPARATOR = keccak256(abi.encode(
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
EIP712_DOMAIN_TYPEHASH,
keccak256("Example.SignIn"),
keccak256("1"),
block.chainid,
address(this)
));
}

struct SignIn {
address user;
uint32 time;
SignatureRSV rsv;
}

modifier authenticated(SignIn calldata auth)
{
// Must be signed within 24 hours ago
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
require( auth.time > (block.timestamp - (60*60*24)) );

// Validate EIP-712 sign-in authentication
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
bytes32 authdataDigest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
SIGNIN_TYPEHASH,
auth.user,
auth.time
))
));

require( auth.user == ecrecover(authdataDigest, uint8(auth.rsv.v), auth.rsv.r, auth.rsv.s), "Invalid Sign-In" );

_;
}

function authenticatedViewCall(
SignIn calldata auth,
... args
)
external view
authenticated(auth)
returns (bytes memory output)
{
// Use `auth.user` instead of `msg.sender`
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

Then the frontend dApp can request the user to sign-in using EIP-712, you may wish to add additional parameters which are authenticated such as the domain name. The following code example uses Ethers:
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

```typescript
const time = new Date().getTime();
const user = await eth.signer.getAddress();

// Ask user to 'Sign-In' every 24 hours
const signature = await eth.signer._signTypedData({
name: "Example.SignIn",
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
version: "1",
chainId: import.meta.env.CHAINID,
verifyingContract: contract.address
}, {
SignIn: [
{ name: 'user', type: "address" },
{ name: 'time', type: 'uint32' },
]
}, {
user: user,
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
time: time
});
const rsv = ethers.utils.splitSignature(signature);
const auth = {user, time, rsv};
// TODO: cache the result
CedarMist marked this conversation as resolved.
Show resolved Hide resolved

// Then in future, authenticated view calls can be performed using the authenticated data
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
await contract.authenticatedViewCall(auth, ...args);
```
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions sidebarDapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const sidebars = {
'dapp/sapphire/guide',
'dapp/sapphire/browser',
'dapp/sapphire/precompiles',
'dapp/sapphire/authentication',
CedarMist marked this conversation as resolved.
Show resolved Hide resolved
'dapp/sapphire/addresses',
],
},
Expand Down