Skip to main content

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

  1. 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
  2. Using the PKP Session Signatures, the SDK will make a request to the Lit network to execute the exportPrivateKey Lit Action
  3. 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
  4. 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

Source code

/** 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:

note

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

danger

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 install \
@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.

note

The expiration used for the Auth Method must be 10 minutes or less to be valid.

note

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

danger

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.