Maximized confidentiality
This page documents all the technical details Mirage puts in place for ensuring maximized confidentiality.
The confidential EVM runtime
The fundamental mechanism upon which Mirage builds all its confidentiality is the Sapphire EVM runtime (opens in a new tab). In a nutshell, the Sapphire EVM works by executing all transactions inside Intel SGX (opens in a new tab) secure enclaves, where encrypted contract storage (including keys and values) and transaction calldata can be securely decrypted and processed. Since the private key capable of decrypting state and calldata is generated in and only replicated between secure enclaves, no one, including the validators, would have access to the decrypted data.
Effectively, Sapphire offers a trustless black box for transitions between encrypted states. There exist other approaches for achieving similar results, most notably zero-knowledge proofs and fully homomorphic encryption. Unlike these approaches, Sapphire incurs significantly smaller performance overhead, making it more scalable.
Attack vectors and mitigations
Just having the confidential EVM runtime is far from sufficient for building a confidential decentralized application. There exist many attack vectors that could lead to privacy leaks despite the use of the confidential runtime, which is why it's way more complicated to build a truly native Sapphire application than simply forking existing Solidity contracts.
This section lists a few such potential attack vectors, each of which is accompanied by an explanation of how Mirage mitigates/works around it.
Storage-based side-channel attacks
Since Sapphire encrypts contract storage keys and values, it might seem to be the case that interaction with storage is risk-free. Unfortunately, this is not true.
Based on the storage key encryption implementation (opens in a new tab) as of this writing, encrypted storage keys are deterministic (for a good reason; otherwise it wouldn't be possible to read previously stored values), which means that it's possible to determine whether 2 different transactions updated the same storage slot with just access to the ciphertext before and after the state transitions.
While this is not a problem for certain applications (e.g. dead man's switch), it's critical for privacy-focused ones like Mirage. Imagine that if deposit and borrow transactions would update the same user-specific slots, it then becomes possible to associate lenders and borrowers by analyzing storage updates (after ruling out global slots that are updated by all transactions).
Mitigations
Mirage has taken the following steps to mitigate the risk of leaking user identity from storage updates:
-
Complete isolation of collateral and debt storage
In smart contract development, it's not uncommon to pack storage data tightly together, as storage is typically the most expensive operations. For example, Aave utilizes a user-specific bitmap for flagging whether a user has active collateral or debt. While such an implementation greatly increases gas efficiency, such a look-up map would become a vulnerability as far as privacy is concerned.
Mirage ensures that the lender side (i.e.
deposit/withdraw) never shares any user-specific storage slots with the borrower side (i.e.borrow/repay). -
Limiting impact to borrowing the same token
As mentioned above, Mirage isolates lender and borrow storage. Unfortunately, within the borrower's side, it's deemed impractical to completely isolate each borrowing transaction, as the system needs to somehow maintain a user's debt balance.
Mirage mitigates this by limiting such exposure to borrowing transactions of the same token. Borrowing different tokens under the same lender account won't be associated.
Unauthorized actions
Some user actions might seem harmless and irrational, so most non-confidential applications wouldn't bother blocking them. For example, a lending protocol might just allow user debts to be repaid by anyone - after all, most users wouldn't mind free money.
However, such actions might actually leak confidential information. An attacker could attempt to repay a very small amount of debt for all lenders of the protocol to determine the set of active borrowers. This works as an attempted repayment for non-existent debt would fail. By observing the set of active borrowers over time, it becomes possible to identify new borrowers.
Mitigations
Mirage guards any action that could access user debt data with EIP-712 authentication. Users must explicitly authorize these actions for them to be taken.
The only exception of this rule is liquidations. It's impossible to simply guard liquidations as by definition they happen without the user's consent. For liquidations, Mirage selectively exposes unhealthy positions, while still preventing side-channel attacks.
Gas-based side-channel attacks
For many applications, it's sufficient to simply block unauthorized actions, and user data would be protected. However, other applications might have actions that cannot be blocked. An example of this is liquidations in money market protocols. Liquidators must obtain information about unhealthy positions in order to perform liquidations, yet the protocol must ensure that no data are leaked for healthy positions via the function(s) used for liquidation discovery.
This is where side-channel attacks come into play. If a Sapphire smart contract naively calculates user health data and returns the result, an attacker may be able to measure the gas consumption of the vulnerable functions and determine whether a user has debt.
For example, a sane implementation of user health analysis is likely to skip fetching prices from the oracle if the user has no deposit or debt for a particular token. This creates a difference in gas usage that can be exploited to reveal the set of users with debt, and hence expose all borrower identities by observing the set over time.
A seemingly "obvious" solution would be to revert on healthy positions,
instead of returning bool, as the Sapphire runtime would pad gas consumption
for failed transactions, effectively making them indistinguishable. This is
not actually safe as the attacker can simply measure gas usage from a contract
instead.
Unlike storage-based side-channel attacks, which can only be carried out by validators, gas-based ones can be done by anyone with RPC access. Gas-based side-channel vulnerabilities are therefore much more susceptible to exploitation.
Mitigations
This issue is similar to those found in cryptography implementations, which are typically solved with constant-time algorithms. Therefore, Mirage pioneers the use of constant-gas functions, which always consume the same amount of gas regardless of input. These functions are unit-tested to ensure their gas constantness.
Liquidation-related functions in Mirage are always either fully constant-gas, or at least constant-gas up to the point at which healthy positions revert. This ensures that an attacker cannot possibly exploit liquidation functions to determine whether a healthy user has debt or not.
See this page for more details regarding liquidations.
Out-of-band privacy leaks
While Sapphire protocols strive to maximize confidentiality within the application, little can be done to stop users from leaking information outside. For example, a user might use a confidential AMM to swap from address A to address B, but address B itself was boostrapped from address A. While a third party cannot tell for sure that the swap transaction was indeed for address B, they can reasonably conclude that it's highly likely the case.
Mitigations
As mentioned, there's little protocols can do to stop what users do to leak their financial activities. What Mirage can do, however, are the following:
-
Reducing the need for privacy-compromising activities
The Mirage team is working on gas relayer integration that will make it possible to borrow the native currency into a completely fresh address, eliminating the need to fund the borrower account in the first place, solving the chicken or egg problem.
-
Privacy best practices reminders
Currently, the Mirage user interface displays a warning when users attempt to borrow/repay from the lender account, as doing this not only leaks the lender-borrower link, but also reduces the size of the overall anonymity set across the whole protocol.
In the future, Mirage could utilize data analysis techniques to identify wallet addresses that are known to be related, and show more insightful tips to help users avoid making mistakes.