Appearance
Architecture
To enable delegated actions, Privy uses Shamir's secret sharing (SSS) within trusted execution environments (TEEs). The combination of these primitives ensure that private keys can only ever be reassembled within a secure enclave and that only permissioned actions can take place.
Trusted execution environments
Trusted execution environments (TEEs), also known as secure enclaves, are highly restricted, isolated compute environments that allow for secure code execution and cryptographic verification (attestation) of the code being executed. In particular, Privy uses AWS Nitro Enclaves.
Privy uses TEEs to support private key reconstitution for delegated actions for the following hardware guarantees:
- TEEs have no persistent storage, no interactive access, and no network connectivity, and so provide a secure, isolated compute environment for sensitive data. Once a user grants permission for delegated actions, approved signatures can only be generated within a TEE. Private keys are only accessible within the TEE for the duration that user grants access.
- Attestations are cryptographic verifications of the computation run on a TEE. They are signed hashes of code on a TEE that can be verified with the corresponding public key. They prove that only the intended code can be executed, guaranteeing that user permissions are enforced for any actions that run in the TEE.
Authorization keypairs
Your app can optionally register an authorization keypair with the TEE to require the TEE to verify a signature from your server before executing any requests. This is strongly recommended as an additional layer of security to ensure that the TEE only executes requests originating from your servers.
Privy uses P-256 (also known as secp256r1) asymmetric keys for authorization keypairs. When you register a keypair:
- The private key is generated on your device, and is only ever known to your app. Neither Privy nor the TEE ever sees the P-256 private key, and cannot sign payloads with it.
- The public key is registered with the TEE, and is used to verify signatures produced by your servers.
If an authorization keypair is registered for your app, the TEE will require that your servers sign the request payload for any wallet action (e.g. signing a message) with the authorization private key for your app. The TEE will verify the signature against the corresponding authorization public key registered for your app before executing any wallet actions.
Shamir's secret sharing
Privy's cryptosystem architecture is built on Shamir's secret sharing (SSS), a reliable and cryptographically battle-tested method for splitting a secret across a number of different parties.
INFO
Privy's shamir-secret-sharing
cryptography library is open-source, heavily audited, and used to secure millions of user accounts. It is the canonical Typescript library for Shamir's secret sharing.
When a user consents to delegated actions, Privy resplits the user's private key on the user's device into a new set of Shamir shares:
- a TEE share, which is encrypted with the TEE's public key, and can only be decrypted within the TEE.
- an app share, which is encrypted by Privy at rest, and is sent to the TEE whenever an action is requested from the wallet.
This is a 2-of-2 share set, which means that both shares are required in order to generate signatures. Neither the app share nor the TEE share in isolation provide any information or access to a user's wallet.
TIP
Only the TEE can decrypt its own share and combine it with the app share to reconstitute a user's wallet and execute actions. Neither Privy nor your app's backend can reconstitute a user's wallet without the TEE in the loop.
Simply put, you can think of delegated actions as the user provisioning an additional device (e.g. the TEE) on which to use their embedded wallet.
Permissions
Users can delegate specific permissions to restrict actions that your app can take on their behalf, such as generating signatures or sending transactions. This is important for features such as payment subscriptions, stop and limit orders, or scheduled transactions.
Actions can be configured with specific permissions, such as:
- Duration of access to the wallet (e.g. one time, one week, six months)
- Transaction maximums
- Action types (transfer only, specific contract interactions)
Before your app can take delegated actions, a user must approve and give permission to your app to take actions on their behalf.
End-to-end flow
At a high-level, the end-to-end flow for delegating actions from a user's embedded wallet is as follows:
Granting consent
- App prompts user to consent to delegating certain permissions over their wallet to allow the app to take certain actions on their behalf.
- If the user consents, Privy resplits the user's private key on their device into an app share and a TEE share which is encrypted with the TEE's public key. The app share and encrypted TEE share are then sent to the Privy backend.
Taking actions on a user's behalf
The flow below assumes that your user has already granted consent for certain permissions over their wallet.
- Your app makes a
POST
request to Privy's/api/v1/delegated_actions/rpc
endpoint with the user's walletaddress
, walletchain_type
, andrpc
request encoding the action requested from the wallet (e.g. a certain signature).- If an authorization keypair is registered for your app, you must sign the body of the request with your app's authorization private key and include the signature in the request's headers.
- Privy's backend authenticates the request with your Privy app secret. If the request is valid, the request is forwarded to the TEE.
- The TEE decrypts the encrypted TEE share and combines it with the app share to reconstitute the user's private key.
- If an authorization keypair is registered for your app, the TEE will verify the authorization signature from your request against your app's authorization public key before executing any wallet actions.
- The TEE then verifies that the scope of the action requested falls within the user's delegated permissions, and if so, signs the action. This signature is returned in Privy's response to your app's original
POST
request to allow your app to use the signature as needed (e.g. broadcasting a signed transaction).