Exporting a Wrapped Key
This guide covers the exportPrivateKey
function from the Wrapped Keys SDK. For an overview of what a Wrapped Key is and what can be done with it, please go here.
Using the exportPrivateKey
function, you can export existing Wrapped Keys to decrypt and obtain their underlying private keys. The Wrapped Keys SDK will look up the corresponding encryption metadata (ciphertext
and dataToEncryptHash
) for your PKP in Lit's private DynamoDB instance. If found, it well then use your provided PKP Session Signatures to authorize decryption of the private key, and will return it to you in clear text.
Below we will walk through an implementation of exportPrivateKey
. The full code implementation can be found here.
Overview of How it Works
- The Wrapped Keys SDK will use the provided Wrapped Key ID and PKP Session Signatures to fetch the encryption metadata for a specific Wrapped Key
- Using the PKP Session Signatures, the SDK will make a request to the Lit network to execute the exportPrivateKey Lit Action
- The Lit Action will check the Access Control Conditions used to encrypt the plaintext private key to verify whether the PKP is authorized to decrypt the private key
- If authorized, the unencrypted plaintext private key will be returned. If not authorized, an error will be returned
Prerequisites
Before continuing with this guide, you should have an understanding of:
exportPrivateKey
's Interface
/** Exports a previously persisted private key from the wrapped keys service for direct use by the caller, along with the keys metadata.
* This method fetches the encrypted key from the wrapped keys service, then executes a Lit Action that decrypts the key inside the LIT action and
* removes the salt from the decrypted key.
*/
export async function exportPrivateKey(
params: {
pkpSessionSigs: SessionSigsMap;
litNodeClient: ILitNodeClient;
network: 'evm' | 'solana'
id: string,
}
): Promise<{
pkpAddress: string;
decryptedPrivateKey: string;
publicKey: string;
litNetwork: LIT_NETWORKS_KEYS;
keyType: 'K256' | 'ed25519';
memo: string;
id: string;
}>
Parameters
pkpSessionSigs
When a Wrapped Key is generated, it's encrypted with the following Access Control Conditions:
[
{
contractAddress: '',
standardContractType: '',
chain: CHAIN_ETHEREUM,
method: '',
parameters: [':userAddress'],
returnValueTest: {
comparator: '=',
value: pkpAddress,
},
},
];
where pkpAddress
is the addressed derived from the pkpSessionSigs
. This restricts the decryption of the Wrapped Key to only those whom can generate valid Authentication Signatures from the PKP which generated the Wrapped Key.
A valid pkpSessionSigs
object can be obtained using the getPkpSessionSigs helper method available on an instance of LitNodeClient. We dive deeper into obtaining a pkpSessionSigs
using getPkpSessionSigs
in the Generating PKP Session Signatures section of this guide.
litNodeClient
This is an instance of the LitNodeClient that is connected to a Lit network.
network
This parameter dictates what Lit Action is used to decrypt the private key. It must be one of the supported Wrapped Keys Networks which currently consists of:
Currently no matter what network is specified, this Lit Action code will be used to decrypt the private key, regardless if the underlying private key is derived via K256
or ed25519
.
evm
solana
id
This is the unique identifier (UUID v4) generated by Lit for the Wrapped Key, when it's either imported or generated.
Because a PKP can have multiple Wrapped Keys attached to it, this ID is required to correctly identify which Wrapped Key to export.
Return Value
exportPrivateKey
will return a ExportPrivateKeyResult object after it successfully retrieves and decrypts the Wrapped Key.
import { LIT_NETWORKS_KEYS } from '@lit-protocol/types';
/** Includes the decrypted private key and metadata that was stored alongside it in the wrapped keys service
*
* @typedef ExportPrivateKeyResult
* @property { LIT_NETWORKS_KEYS } litNetwork The LIT network that the LIT Node Client was configured for when the key was persisted to the wrapped keys service
* @property { string } decryptedPrivateKey The decrypted, plain text private key that was persisted to the wrapped keys service
* @property { string } pkpAddress The LIT PKP Address that the key was linked to; this is derived from the provided pkpSessionSigs
* @property { string } publicKey The public key of the key being imported into the wrapped keys service
* @property { string } keyType The algorithm type of the key; this might be K256, ed25519, or other key formats. The `keyType` will be included in the metadata returned from the wrapped keys service
* @property { string } memo A (typically) user-provided descriptor for the encrypted private key
* @property { string } id The unique identifier (UUID V4) of the encrypted private key
*/
export interface ExportPrivateKeyResult {
pkpAddress: string;
decryptedPrivateKey: string;
publicKey: string;
litNetwork: LIT_NETWORKS_KEYS;
keyType: 'K256' | 'ed25519';
memo: string;
id: string;
}
pkpAddress
This is the Ethereum address for the PKP that is associated with the Wrapped Key i.e. the PKP that created the Session Signatures when the Wrapped Key was imported/generated, and used for encrypting the private key. The address is derived from the provided pkpSessionSigs
.
decryptedPrivateKey
This return value is the private key unencrypted and clear text.
Be mindful when and where you're calling this method as to not expose the private key to anyone who should not have direct access to it.
This is the underlying private key for the Wrapped Key that was either imported into the Lit network, or generated by a Wrapped Keys Lit Action.
publicKey
This is the corresponding public key for the wrapped private key that was either imported into the Lit network, or generated by a Wrapped Keys Lit Action.
litNetwork
This is the Lit network that the LitNodeClient
was connected to when the Wrapped Key was created.
keyType
This is the algorithm used to generate the underlying private key for the Wrapped Key.
memo
This is the additional identifier/descriptor string set for the Wrapped key when it was imported, generated, or stored.
This parameter is an arbitrary string that can be used as an additional identifier or descriptor of the encrypted private key.
id
This is a unique identifier (UUID v4) generated by Lit for the Wrapped Key.
Because a PKP can have multiple Wrapped Keys attached to it, this ID is used to identify which Wrapped Key to use when calling other Wrapped Key methods such as signMessageWithEncryptedKey and signTransactionWithEncryptedKey.
Example Implementation
Now that we know what the exportPrivateKey
function does, it's parameters, and it's return values, let's now dig into a complete implementation.
The full code implementation can be found here.
Installing the Required Dependencies
- npm
- yarn
npm install \
@lit-protocol/auth-helpers \
@lit-protocol/constants \
@lit-protocol/lit-auth-client \
@lit-protocol/lit-node-client \
@lit-protocol/wrapped-keys \
ethers@v5
yarn add \
@lit-protocol/auth-helpers \
@lit-protocol/constants \
@lit-protocol/lit-auth-client \
@lit-protocol/lit-node-client \
@lit-protocol/wrapped-keys \
ethers@v5
Instantiating an Ethers Signer
The ETHEREUM_PRIVATE_KEY
environment variable is required. The corresponding Ethereum address needs to have ownership of the PKP we will be using to generate the pkpSessionSigs
.
import * as ethers from 'ethers';
import { LIT_RPC } from "@lit-protocol/constants";
const ethersSigner = new ethers.Wallet(
process.env.ETHEREUM_PRIVATE_KEY,
new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE)
);
Instantiating a LitNodeClient
Here we are instantiating an instance of LitNodeClient
and connecting it to the datil-dev
Lit network.
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LitNetwork } from "@lit-protocol/constants";
const litNodeClient = new LitNodeClient({
litNetwork: LitNetwork.DatilDev,
debug: false,
});
await litNodeClient.connect();
Generating PKP Session Signatures
The LIT_PKP_PUBLIC_KEY
environment variable is required. This PKP should be owned by the corresponding Ethereum address for the ETHEREUM_PRIVATE_KEY
environment variable.
The PKP's Ethereum address will be used for the Access Control Conditions used to encrypt the generated private key, and by default, will be the only entity able to authorize decryption of the private key.
The expiration
used for the Auth Method must be 10 minutes or less to be valid.
The Auth Method used in this example implementation is signing a Sign in With Ethereum (EIP-4361) message using an Externally Owned Account (EOA), but any Auth Method can be used to authenticate with Lit to get PKP Session Signatures.
import { EthWalletProvider } from "@lit-protocol/lit-auth-client";
import {
LitAbility,
LitActionResource,
LitPKPResource,
} from "@lit-protocol/auth-helpers";
const pkpSessionSigs = await litNodeClient.getPkpSessionSigs({
pkpPublicKey: process.env.LIT_PKP_PUBLIC_KEY,
authMethods: [
await EthWalletProvider.authenticate({
signer: ethersSigner,
litNodeClient,
expiration: new Date(Date.now() + 1000 * 60 * 10).toISOString(), // 10 minutes
}),
],
resourceAbilityRequests: [
{
resource: new LitActionResource("*"),
ability: LitAbility.LitActionExecution,
},
],
expiration: new Date(Date.now() + 1000 * 60 * 10).toISOString(), // 10 minutes
});
Exporting a Wrapped Key
The return value from successfully calling exportPrivateKey
includes the unencrypted private key in clear text.
Be mindful when and where you're calling this method as to not expose the private key to anyone who should not have direct access to it.
Now that we have all that we need, we can call exportPrivateKey
to export the underlying private key for the Wrapped Key:
import { api } from "@lit-protocol/wrapped-keys";
const { exportPrivateKey } = api;
const exportedPrivateKeyResult = await exportPrivateKey({
pkpSessionSigs,
litNodeClient,
id: process.env.WRAPPED_KEY_ID
network: process.env.EVM_OR_SOLANA
});
Summary
The full code implementation can be found here.
After executing the example implementation above, you will have exported the underlying private key for the Wrapped Key that was associated with the PKP that produced the provided pkpSessionSigs
, and had the given id
.