Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions local-tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ import { testSignMessageWithSolanaEncryptedKey } from './tests/wrapped-keys/test
import { testSignTransactionWithSolanaEncryptedKey } from './tests/wrapped-keys/testSignTransactionWithSolanaEncryptedKey';
import { testBatchGeneratePrivateKeys } from './tests/wrapped-keys/testBatchGeneratePrivateKeys';
import { testFailBatchGeneratePrivateKeysAtomic } from './tests/wrapped-keys/testFailStoreEncryptedKeyBatchIsAtomic';
import { testEthereumSignTypedDataWrappedKey } from './tests/wrapped-keys/testEthereumSignTypedDataWrappedKey';
import { testEthereumSignTypedDataGeneratedKey } from './tests/wrapped-keys/testEthereumSignTypedDataGeneratedKey';
import { testFailEthereumSignTypedDataWrappedKeyWithMissingParam } from './tests/wrapped-keys/testFailEthereumSignTypedDataWrappedKeyWithMissingParam';
import { testFailEthereumSignTypedDataWrappedKeyWithInvalidParam } from './tests/wrapped-keys/testFailEthereumSignTypedDataWrappedKeyWithInvalidParam';
import { testFailEthereumSignTypedDataWrappedKeyInvalidDecryption } from './tests/wrapped-keys/testFailEthereumSignTypedDataWrappedKeyInvalidDecryption';

import { setLitActionsCodeToLocal } from './tests/wrapped-keys/util';
import { testUseEoaSessionSigsToRequestSingleResponse } from './tests/testUseEoaSessionSigsToRequestSingleResponse';
Expand Down Expand Up @@ -140,6 +145,10 @@ setLitActionsCodeToLocal();
testEthereumBroadcastTransactionWrappedKey,
testEthereumBroadcastWrappedKeyWithFetchGasParams,

// -- typed data signing
testEthereumSignTypedDataWrappedKey,
testEthereumSignTypedDataGeneratedKey,

// -- generate wrapped keys
testGenerateEthereumWrappedKey,
testGenerateSolanaWrappedKey,
Expand All @@ -160,6 +169,11 @@ setLitActionsCodeToLocal();
testFailEthereumSignTransactionWrappedKeyInvalidDecryption,
testFailBatchGeneratePrivateKeysAtomic,

// -- typed data signing invalid cases
testFailEthereumSignTypedDataWrappedKeyWithMissingParam,
testFailEthereumSignTypedDataWrappedKeyWithInvalidParam,
testFailEthereumSignTypedDataWrappedKeyInvalidDecryption,

// -- import wrapped keys
testFailImportWrappedKeysWithSamePrivateKey,
testFailImportWrappedKeysWithEoaSessionSig,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { log } from '@lit-protocol/misc';
import { ethers } from 'ethers';
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
import { api } from '@lit-protocol/wrapped-keys';
import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs';
import { deriveAddressFromGeneratedPublicKey } from './util';

const { generatePrivateKey, signTypedDataWithEncryptedKey } = api;

/**
* Test Commands:
* ✅ NETWORK=datil-dev yarn test:local --filter=testEthereumSignTypedDataGeneratedKey
* ✅ NETWORK=datil-test yarn test:local --filter=testEthereumSignTypedDataGeneratedKey
* ✅ NETWORK=custom yarn test:local --filter=testEthereumSignTypedDataGeneratedKey
*/
export const testEthereumSignTypedDataGeneratedKey = async (
devEnv: TinnyEnvironment
) => {
const alice = await devEnv.createRandomPerson();

try {
const pkpSessionSigs = await getPkpSessionSigs(
devEnv,
alice,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

const { pkpAddress, id, generatedPublicKey } = await generatePrivateKey({
pkpSessionSigs,
network: 'evm',
litNodeClient: devEnv.litNodeClient,
memo: 'Test key',
});

const alicePkpAddress = alice.authMethodOwnedPkp.ethAddress;
if (pkpAddress !== alicePkpAddress) {
throw new Error(
`Received address: ${pkpAddress} doesn't match Alice's PKP address: ${alicePkpAddress}`
);
}

const aliceWrappedKeyAddress =
deriveAddressFromGeneratedPublicKey(generatedPublicKey);

const pkpSessionSigsSigning = await getPkpSessionSigs(
devEnv,
alice,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

// Test EIP-712 typed data with different structure
const typedData = {
domain: {
name: 'TestApp',
version: '2',
chainId: 1,
verifyingContract: '0x1234567890123456789012345678901234567890',
},
types: {
Transaction: [
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'data', type: 'bytes' },
{ name: 'operation', type: 'uint8' },
{ name: 'safeTxGas', type: 'uint256' },
{ name: 'baseGas', type: 'uint256' },
{ name: 'gasPrice', type: 'uint256' },
{ name: 'gasToken', type: 'address' },
{ name: 'refundReceiver', type: 'address' },
{ name: 'nonce', type: 'uint256' },
],
},
value: {
to: '0x1234567890123456789012345678901234567890',
value: '0',
data: '0x',
operation: 0,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: '0x0000000000000000000000000000000000000000',
refundReceiver: '0x0000000000000000000000000000000000000000',
nonce: 0,
},
};

const signature = await signTypedDataWithEncryptedKey({
pkpSessionSigs: pkpSessionSigsSigning,
network: 'evm',
messageToSign: typedData,
litNodeClient: devEnv.litNodeClient,
id,
});

if (!ethers.utils.isHexString(signature)) {
throw new Error(`signature isn't hex: ${signature}`);
}

// Verify the signature is valid by recovering the address
const recoveredAddress = ethers.utils.verifyTypedData(
typedData.domain,
typedData.types,
typedData.value,
signature
);

if (
recoveredAddress.toLowerCase() !== aliceWrappedKeyAddress.toLowerCase()
) {
throw new Error(
`Recovered address: ${recoveredAddress} doesn't match Wrapped Key address: ${aliceWrappedKeyAddress}`
);
}

log('✅ testEthereumSignTypedDataGeneratedKey');
} finally {
devEnv.releasePrivateKeyFromUser(alice);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { log } from '@lit-protocol/misc';
import { ethers } from 'ethers';
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
import { api } from '@lit-protocol/wrapped-keys';
import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs';
import { deriveAddressFromGeneratedPublicKey } from './util';

const { importPrivateKey, signTypedDataWithEncryptedKey } = api;

/**
* Test Commands:
* ✅ NETWORK=datil-dev yarn test:local --filter=testEthereumSignTypedDataWrappedKey
* ✅ NETWORK=datil-test yarn test:local --filter=testEthereumSignTypedDataWrappedKey
* ✅ NETWORK=custom yarn test:local --filter=testEthereumSignTypedDataWrappedKey
*/
export const testEthereumSignTypedDataWrappedKey = async (
devEnv: TinnyEnvironment
) => {
const alice = await devEnv.createRandomPerson();

try {
const pkpSessionSigs = await getPkpSessionSigs(
devEnv,
alice,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

const aliceWrappedKey = ethers.Wallet.createRandom();
const privateKey = aliceWrappedKey.privateKey;
const aliceWrappedKeyAddress = aliceWrappedKey.address;

const { pkpAddress, id } = await importPrivateKey({
pkpSessionSigs,
privateKey,
litNodeClient: devEnv.litNodeClient,
publicKey: aliceWrappedKeyAddress,
keyType: 'K256',
memo: 'Test key',
});

const alicePkpAddress = alice.authMethodOwnedPkp.ethAddress;
if (pkpAddress !== alicePkpAddress) {
throw new Error(
`Received address: ${pkpAddress} doesn't match Alice's PKP address: ${alicePkpAddress}`
);
}

const pkpSessionSigsSigning = await getPkpSessionSigs(
devEnv,
alice,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

// Test EIP-712 typed data
const typedData = {
domain: {
name: 'TestApp',
version: '1',
chainId: 1,
verifyingContract: '0x1234567890123456789012345678901234567890',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
value: {
from: {
name: 'Alice',
wallet: alice.wallet.address,
},
to: {
name: 'Bob',
wallet: '0x1234567890123456789012345678901234567890',
},
contents: 'Hello, Bob!',
},
};

const signature = await signTypedDataWithEncryptedKey({
pkpSessionSigs: pkpSessionSigsSigning,
network: 'evm',
messageToSign: typedData,
litNodeClient: devEnv.litNodeClient,
id,
});

// console.log('signature');
// console.log(signature);

if (!ethers.utils.isHexString(signature)) {
throw new Error(`signature isn't hex: ${signature}`);
}

// Verify the signature is valid by recovering the address
const recoveredAddress = ethers.utils.verifyTypedData(
typedData.domain,
typedData.types,
typedData.value,
signature
);

if (
recoveredAddress.toLowerCase() !== aliceWrappedKeyAddress.toLowerCase()
) {
throw new Error(
`Recovered address: ${recoveredAddress} doesn't match PKP address: ${pkpAddress}`
);
}

log('✅ testEthereumSignTypedDataWrappedKey');
} finally {
devEnv.releasePrivateKeyFromUser(alice);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { log } from '@lit-protocol/misc';
import { ethers } from 'ethers';
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
import { api } from '@lit-protocol/wrapped-keys';
import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs';

const { importPrivateKey, signTypedDataWithEncryptedKey } = api;

/**
* Test Commands:
* ✅ NETWORK=datil-dev yarn test:local --filter=testFailEthereumSignTypedDataWrappedKeyInvalidDecryption
* ✅ NETWORK=datil-test yarn test:local --filter=testFailEthereumSignTypedDataWrappedKeyInvalidDecryption
* ✅ NETWORK=custom yarn test:local --filter=testFailEthereumSignTypedDataWrappedKeyInvalidDecryption
*/
export const testFailEthereumSignTypedDataWrappedKeyInvalidDecryption = async (
devEnv: TinnyEnvironment
) => {
const alice = await devEnv.createRandomPerson();

try {
const pkpSessionSigs = await getPkpSessionSigs(
devEnv,
alice,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

const aliceWrappedKey = ethers.Wallet.createRandom();
const privateKey = aliceWrappedKey.privateKey;
const aliceWrappedKeyAddress = aliceWrappedKey.address;

const { pkpAddress, id } = await importPrivateKey({
pkpSessionSigs,
privateKey,
litNodeClient: devEnv.litNodeClient,
publicKey: aliceWrappedKeyAddress,
keyType: 'K256',
memo: 'Test key',
});

const alicePkpAddress = alice.authMethodOwnedPkp.ethAddress;
if (pkpAddress !== alicePkpAddress) {
throw new Error(
`Received address: ${pkpAddress} doesn't match Alice's PKP address: ${alicePkpAddress}`
);
}

// Use a different user's session sigs to try to decrypt the key
const bob = await devEnv.createRandomPerson();
const bobPkpSessionSigs = await getPkpSessionSigs(
devEnv,
bob,
null,
new Date(Date.now() + 1000 * 60 * 10).toISOString()
); // 10 mins expiry

const typedData = {
domain: {
name: 'TestApp',
version: '1',
chainId: 1,
verifyingContract: '0x1234567890123456789012345678901234567890',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
value: {
from: alice.wallet.address,
to: '0x1234567890123456789012345678901234567890',
contents: 'Hello, Bob!',
},
};

try {
const _res = await signTypedDataWithEncryptedKey({
pkpSessionSigs: bobPkpSessionSigs, // Using Bob's session sigs to try to decrypt Alice's key
network: 'evm',
messageToSign: typedData,
litNodeClient: devEnv.litNodeClient,
id, // Alice's key ID
});
} catch (e: any) {
if (e.message.includes('Could not find')) {
console.log('✅ THIS IS EXPECTED: ', e);
console.log(e.message);
console.log(
'✅ testFailEthereumSignTypedDataWrappedKeyInvalidDecryption is expected to have an error'
);
} else {
console.log('ERROR', e.message);
throw e;
}
}

log('✅ testFailEthereumSignTypedDataWrappedKeyInvalidDecryption');
} finally {
devEnv.releasePrivateKeyFromUser(alice);
}
};
Loading