Skip to content

Commit 887dea7

Browse files
Merge pull request #534 from LIT-Protocol/feat-update-wrapped-keys-export
Feat: Update Wrapped keys Export to use Lit Actions
2 parents fb77647 + 3d19136 commit 887dea7

16 files changed

+174
-53
lines changed

local-tests/tests/wrapped-keys/testExportWrappedKey.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const testExportWrappedKey = async (devEnv: TinnyEnvironment) => {
5252
const { decryptedPrivateKey } = await exportPrivateKey({
5353
pkpSessionSigs: pkpSessionSigsExport,
5454
litNodeClient: devEnv.litNodeClient,
55+
network: 'solana',
5556
});
5657

5758
if (decryptedPrivateKey !== privateKey) {

local-tests/tests/wrapped-keys/testGenerateEthereumWrappedKey.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ export const testGenerateEthereumWrappedKey = async (
4949

5050
console.log(pkpSessionSigsExport);
5151

52-
// FIXME: Export broken as we can't decrypt data encrypted inside a Lit Action
5352
const { decryptedPrivateKey } = await exportPrivateKey({
5453
pkpSessionSigs: pkpSessionSigsExport,
5554
litNodeClient: devEnv.litNodeClient,
55+
network: 'evm',
5656
});
5757

5858
const wallet = new ethers.Wallet(decryptedPrivateKey);

local-tests/tests/wrapped-keys/testGenerateSolanaWrappedKey.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { api } from '@lit-protocol/wrapped-keys';
44
import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs';
55
import nacl from 'tweetnacl';
66
import bs58 from 'bs58';
7-
import { ethers } from 'ethers';
7+
import { Keypair } from '@solana/web3.js';
88

99
const { generatePrivateKey, signMessageWithEncryptedKey, exportPrivateKey } =
1010
api;
@@ -83,16 +83,18 @@ export const testGenerateSolanaWrappedKey = async (
8383
new Date(Date.now() + 1000 * 60 * 10).toISOString()
8484
); // 10 mins expiry
8585

86-
// FIXME: Export broken as we can't decrypt data encrypted inside a Lit Action
8786
const { decryptedPrivateKey } = await exportPrivateKey({
8887
pkpSessionSigs: pkpSessionSigsExport,
8988
litNodeClient: devEnv.litNodeClient,
89+
network: 'solana',
9090
});
9191

92-
const wallet = new ethers.Wallet(decryptedPrivateKey);
93-
const decryptedPublicKey = wallet.publicKey;
92+
const solanaKeyPair = Keypair.fromSecretKey(
93+
Buffer.from(decryptedPrivateKey, 'hex')
94+
);
95+
const decryptedPublicKey = solanaKeyPair.publicKey;
9496

95-
if (decryptedPublicKey !== generatedPublicKey) {
97+
if (decryptedPublicKey.toString() !== generatedPublicKey) {
9698
throw new Error(
9799
`Decrypted decryptedPublicKey: ${decryptedPublicKey} doesn't match with the original generatedPublicKey: ${generatedPublicKey}`
98100
);

packages/wrapped-keys/esbuild.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ const esbuild = require('esbuild');
2525
outdir: './src/lib/litActions/ethereum/dist',
2626
inject: ['./buffer.shim.js'],
2727
});
28+
await esbuild.build({
29+
entryPoints: ['./src/lib/litActions/common/src/exportPrivateKey.js'],
30+
bundle: true,
31+
minify: true,
32+
sourcemap: false,
33+
outdir: './src/lib/litActions/common/dist',
34+
inject: ['./buffer.shim.js'],
35+
});
2836
})();
Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,39 @@
1-
import { decryptToString } from '@lit-protocol/encryption';
1+
import { exportPrivateKeyWithLitAction } from '../lit-actions-client';
22

3-
import { CHAIN_ETHEREUM, LIT_PREFIX } from '../constants';
43
import { fetchPrivateKeyMetadata } from '../service-client';
54
import { ExportPrivateKeyParams, ExportPrivateKeyResult } from '../types';
6-
import {
7-
getFirstSessionSig,
8-
getPkpAccessControlCondition,
9-
getPkpAddressFromSessionSig,
10-
} from '../utils';
5+
import { getFirstSessionSig, getPkpAccessControlCondition } from '../utils';
6+
import { getLitActionCid } from '../lit-actions-client/utils';
117

12-
/** Exports a previously persisted private key from the wrapped keys service for direct use by the caller, along with the keys metadata
8+
/**
9+
* Exports a previously persisted private key from the wrapped keys service for direct use by the caller, along with the keys metadata.
10+
* 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
11+
* removes the salt from the decrypted key.
1312
*
1413
* @param { ExportPrivateKeyParams } params Parameters required to export the private key
14+
*
15+
* @returns { Promise<ExportPrivateKeyResult> } - The decrypted private key of the Wrapped Key along with all the associated key info and LIT PKP Address associated with the Wrapped Key
1516
*/
1617
export async function exportPrivateKey(
1718
params: ExportPrivateKeyParams
1819
): Promise<ExportPrivateKeyResult> {
19-
const { pkpSessionSigs, litNodeClient } = params;
20+
const { litNodeClient, network, pkpSessionSigs } = params;
2021

2122
const sessionSig = getFirstSessionSig(pkpSessionSigs);
22-
const pkpAddress = getPkpAddressFromSessionSig(sessionSig);
23-
const allowPkpAddressToDecrypt = getPkpAccessControlCondition(pkpAddress);
24-
25-
const privateKeyMetadata = await fetchPrivateKeyMetadata({
23+
const storedKeyMetadata = await fetchPrivateKeyMetadata({
2624
sessionSig,
2725
litNetwork: litNodeClient.config.litNetwork,
2826
});
2927

30-
const { ciphertext, dataToEncryptHash, ...privateKeyMetadataMinusEncrypted } =
31-
privateKeyMetadata;
32-
33-
const decryptedPrivateKey = await decryptToString(
34-
{
35-
accessControlConditions: [allowPkpAddressToDecrypt],
36-
chain: CHAIN_ETHEREUM,
37-
ciphertext,
38-
dataToEncryptHash,
39-
sessionSigs: pkpSessionSigs,
40-
},
41-
litNodeClient
28+
const allowPkpAddressToDecrypt = getPkpAccessControlCondition(
29+
storedKeyMetadata.pkpAddress
4230
);
4331

44-
// It will be of the form lit_<privateKey>
45-
if (!decryptedPrivateKey.startsWith(LIT_PREFIX)) {
46-
throw new Error(
47-
`PKey was not encrypted with salt; all wrapped keys must be prefixed with '${LIT_PREFIX}'`
48-
);
49-
}
50-
51-
return {
52-
decryptedPrivateKey: decryptedPrivateKey.slice(LIT_PREFIX.length),
53-
...privateKeyMetadataMinusEncrypted,
54-
};
32+
return exportPrivateKeyWithLitAction({
33+
...params,
34+
litActionIpfsCid: getLitActionCid(network, 'exportPrivateKey'),
35+
accessControlConditions: [allowPkpAddressToDecrypt],
36+
pkpSessionSigs,
37+
storedKeyMetadata,
38+
});
5539
}

packages/wrapped-keys/src/lib/lit-actions-client/constants.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ import { LitCidRepository } from './types';
22

33
const LIT_ACTION_CID_REPOSITORY: LitCidRepository = Object.freeze({
44
signTransaction: Object.freeze({
5-
evm: 'QmdYUhPCCK5hpDWMK1NiDLNLG6RZQy61QE4J7dBm1Y2nbA',
6-
solana: 'QmSi9GL2weCFEP1SMAUw5PDpZRr436Zt3tLUNrSECPA5dT',
5+
evm: 'QmSsgmy1N1zFZ5yNPCY7QWQZwrYRLuYUJF1VDygJc7L26o',
6+
solana: 'QmdkcMmrtqWSQ8VrPr8KwzuzZnAxGJoVDeZP3NKTWCMZCg',
77
}),
88
signMessage: Object.freeze({
9-
evm: 'QmTMGcyp77NeppGaqF2DmE1F8GXTSxQYzXCrbE7hNudUWx',
10-
solana: 'QmUxnWS8VU9QwZRdyfwsEtvhJZcvcdrannjokgZoA1sesy',
9+
evm: 'QmWVW51FBH5j3wwaMVy8MR1QyzJgEjuaPh1yqwSGXRCENx',
10+
solana: 'QmSPcfFhLofjhNDd5ZdVbhw53zmbR8oV4C2585Bm7C8izH',
1111
}),
1212
generateEncryptedKey: Object.freeze({
1313
evm: 'QmaoPMSqcze3NW3KSA75ecWSkcmWT1J7kVr8LyJPCKRvHd',
1414
solana: 'QmdRBXYLYvcNHrChmsZ2jFDY8dA99CcSdqHo3p1ES3UThL',
1515
}),
16+
exportPrivateKey: Object.freeze({
17+
evm: 'Qmb5ZAm1EZRL7dYTtyYxkPxx4kBmoCjjzcgdrJH9cKMXxR',
18+
solana: 'Qmb5ZAm1EZRL7dYTtyYxkPxx4kBmoCjjzcgdrJH9cKMXxR',
19+
}),
1620
});
1721

1822
export { LIT_ACTION_CID_REPOSITORY };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { AccessControlConditions } from '@lit-protocol/types';
2+
3+
import { postLitActionValidation } from './utils';
4+
import { ExportPrivateKeyParams, StoredKeyMetadata } from '../types';
5+
6+
interface SignMessageWithLitActionParams extends ExportPrivateKeyParams {
7+
accessControlConditions: AccessControlConditions;
8+
storedKeyMetadata: StoredKeyMetadata;
9+
litActionIpfsCid: string;
10+
}
11+
12+
export async function exportPrivateKeyWithLitAction(
13+
args: SignMessageWithLitActionParams
14+
) {
15+
const {
16+
accessControlConditions,
17+
litNodeClient,
18+
pkpSessionSigs,
19+
litActionIpfsCid,
20+
storedKeyMetadata,
21+
} = args;
22+
23+
const {
24+
pkpAddress,
25+
ciphertext,
26+
dataToEncryptHash,
27+
...storeKeyMetadataMinusEncryptedAndPkp
28+
} = storedKeyMetadata;
29+
const result = await litNodeClient.executeJs({
30+
sessionSigs: pkpSessionSigs,
31+
ipfsId: litActionIpfsCid,
32+
jsParams: {
33+
pkpAddress,
34+
ciphertext,
35+
dataToEncryptHash,
36+
accessControlConditions,
37+
},
38+
});
39+
40+
const decryptedPrivateKey = postLitActionValidation(result);
41+
42+
return {
43+
decryptedPrivateKey,
44+
pkpAddress,
45+
...storeKeyMetadataMinusEncryptedAndPkp,
46+
};
47+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { generateKeyWithLitAction } from './generate-key';
22
import { signMessageWithLitAction } from './sign-message';
33
import { signTransactionWithLitAction } from './sign-transaction';
4+
import { exportPrivateKeyWithLitAction } from './export-private-key';
45

56
export {
67
generateKeyWithLitAction,
78
signTransactionWithLitAction,
89
signMessageWithLitAction,
10+
exportPrivateKeyWithLitAction,
911
};

packages/wrapped-keys/src/lib/lit-actions-client/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { Network } from '../types';
33
export type LitActionType =
44
| 'signTransaction'
55
| 'signMessage'
6-
| 'generateEncryptedKey';
6+
| 'generateEncryptedKey'
7+
| 'exportPrivateKey';
78

89
export type LitCidRepositoryEntry = Readonly<Record<Network, string>>;
910

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const { removeSaltFromDecryptedKey } = require('../../utils');
2+
3+
/**
4+
*
5+
* Exports the private key after decrypting and removing the salt from it.
6+
*
7+
* @jsParam pkpAddress - The Eth address of the PKP which is associated with the Wrapped Key
8+
* @jsParam ciphertext - For the encrypted Wrapped Key
9+
* @jsParam dataToEncryptHash - For the encrypted Wrapped Key
10+
* @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key
11+
*
12+
* @returns { Promise<string> } - Returns a decrypted private key.
13+
*/
14+
15+
(async () => {
16+
let decryptedPrivateKey;
17+
try {
18+
decryptedPrivateKey = await Lit.Actions.decryptToSingleNode({
19+
accessControlConditions,
20+
ciphertext,
21+
dataToEncryptHash,
22+
chain: 'ethereum',
23+
authSig: null,
24+
});
25+
} catch (err) {
26+
const errorMessage =
27+
'Error: When decrypting to a single node- ' + err.message;
28+
Lit.Actions.setResponse({ response: errorMessage });
29+
return;
30+
}
31+
32+
if (!decryptedPrivateKey) {
33+
// Exit the nodes which don't have the decryptedData
34+
return;
35+
}
36+
37+
try {
38+
const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey);
39+
Lit.Actions.setResponse({ response: privateKey });
40+
} catch (err) {
41+
Lit.Actions.setResponse({ response: err.message });
42+
}
43+
})();

0 commit comments

Comments
 (0)