Skip to content

Commit

Permalink
Export private keys from HD wallet addresses (#3253)
Browse files Browse the repository at this point in the history
### What

Let's allow exporting private keys from HD wallet addresses.

### Testing

- [x] add HD wallet with the mnemonic
- [x] export private key for account from HD wallet
- [x] export the same private key with metamask and compare them


![image](https://user-images.githubusercontent.com/20949277/231476213-bc482747-a91d-4a18-9eed-21254db91e0c.png)


Latest build:
[extension-builds-3253](https://github.com/tahowallet/extension/suites/12348519892/artifacts/655435412)
(as of Wed, 19 Apr 2023 17:38:25 GMT).
  • Loading branch information
kkosiorowska authored Apr 21, 2023
2 parents 46def07 + b378b62 commit 0b885b7
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 41 deletions.
2 changes: 1 addition & 1 deletion background/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@ledgerhq/hw-transport": "^6.20.0",
"@ledgerhq/hw-transport-webusb": "^6.20.0",
"@redux-devtools/remote": "^0.7.4",
"@tallyho/hd-keyring": "0.4.0",
"@tallyho/hd-keyring": "0.5.0",
"@tallyho/provider-bridge-shared": "0.0.1",
"@tallyho/window-provider": "0.0.1",
"@types/w3c-web-usb": "^1.0.5",
Expand Down
12 changes: 10 additions & 2 deletions background/services/keyring/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,16 @@ export default class KeyringService extends BaseService<Events> {
this.requireUnlocked()

try {
const privateKeyWallet = await this.#findPrivateKey(address)
return privateKeyWallet.privateKey
const signerWithType = await this.#findSigner(address)

if (isPrivateKey(signerWithType)) {
return signerWithType.signer.privateKey
}

return signerWithType.signer.exportPrivateKey(
address,
"I solemnly swear that I am treating this private key material with great care."
)
} catch (e) {
return null
}
Expand Down
63 changes: 49 additions & 14 deletions background/services/keyring/tests/index.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,6 @@ describe("Keyring Service", () => {

expect(keyrings.length).toBe(1)
})
it("should be able to export mnemonic", async () => {
const mnemonic = await keyringService.exportMnemonic(
HD_WALLET_MOCK.addresses[0]
)

expect(mnemonic).toBe(HD_WALLET_MOCK.mnemonic)
})
it("should be able to sign transaction", async () => {
const address = HD_WALLET_MOCK.addresses[0]
const signed = await keyringService.signTransaction(
Expand Down Expand Up @@ -203,13 +196,6 @@ describe("Keyring Service", () => {
})
expect(keyringService.getPrivateKeys().length).toBe(1)
})
it("should be able to export private key", async () => {
const privateKey = await keyringService.exportPrivateKey(
PK_WALLET_MOCK.address
)

expect(privateKey).toBe(PK_WALLET_MOCK.privateKey)
})
it("should be able to sign transaction", async () => {
const { address } = PK_WALLET_MOCK
const signed = await keyringService.signTransaction(
Expand Down Expand Up @@ -245,4 +231,53 @@ describe("Keyring Service", () => {
expect(signed).toBeDefined()
})
})

describe("export secrets", () => {
beforeEach(async () => {
await keyringService.importSigner({
type: SignerTypes.privateKey,
privateKey: PK_WALLET_MOCK.privateKey,
})
await keyringService.importSigner({
type: SignerTypes.keyring,
mnemonic: HD_WALLET_MOCK.mnemonic,
source: "import",
})
})
it("should be able to export private key", async () => {
const privateKey = await keyringService.exportPrivateKey(
PK_WALLET_MOCK.address
)

expect(privateKey).toBe(PK_WALLET_MOCK.privateKey)
})
it("should be able to export mnemonic", async () => {
const mnemonic = await keyringService.exportMnemonic(
HD_WALLET_MOCK.addresses[0]
)

expect(mnemonic).toBe(HD_WALLET_MOCK.mnemonic)
})
it("should be able to export private key from HD wallet addresses", async () => {
const privateKey = await keyringService.exportPrivateKey(
HD_WALLET_MOCK.addresses[0]
)

expect(privateKey).toBe(PK_WALLET_MOCK.privateKey) // first address from both mocks is the same
})
it("should require wallet to be unlocked to export secrets", async () => {
keyringService.lock()

const errorMessage = "KeyringService must be unlocked."
const exportMnemonic = async () => {
await keyringService.exportMnemonic(HD_WALLET_MOCK.addresses[0])
}
const exportPrivateKey = async () => {
await keyringService.exportPrivateKey(PK_WALLET_MOCK.address)
}

expect(exportMnemonic()).rejects.toThrowError(errorMessage)
expect(exportPrivateKey()).rejects.toThrowError(errorMessage)
})
})
})
41 changes: 21 additions & 20 deletions ui/components/AccountItem/AccountItemOptionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ type AccountItemOptionsMenuProps = {
accountType: AccountType
}

const allowExportPrivateKeys = [
AccountType.PrivateKey,
AccountType.Imported,
AccountType.Internal,
]

export default function AccountItemOptionsMenu({
accountTotal,
accountType,
Expand All @@ -35,25 +41,9 @@ export default function AccountItemOptionsMenu({
dispatch(setSnackbarMessage("Address copied to clipboard"))
}, [address, dispatch])

const getCustomOptions = useCallback(() => {
switch (accountType) {
case AccountType.PrivateKey:
return isEnabled(FeatureFlags.SUPPORT_PRIV_KEYS)
? [
{
key: "key",
icon: "icons/s/key.svg",
label: t("showPrivateKey.header"),
onClick: () => {
setShowPrivateKeyMenu(true)
},
},
]
: []
default:
return []
}
}, [accountType, t])
const canExportPrivateKey =
isEnabled(FeatureFlags.SUPPORT_PRIV_KEYS) &&
allowExportPrivateKeys.includes(accountType)

return (
<div className="options_menu_wrap">
Expand Down Expand Up @@ -142,7 +132,18 @@ export default function AccountItemOptionsMenu({
copyAddress()
},
},
...getCustomOptions(),
...(canExportPrivateKey
? [
{
key: "key",
icon: "icons/s/key.svg",
label: t("showPrivateKey.header"),
onClick: () => {
setShowPrivateKeyMenu(true)
},
},
]
: []),
{
key: "remove",
icon: "garbage@2x.png",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3300,10 +3300,10 @@
dependencies:
defer-to-connect "^1.0.1"

"@tallyho/hd-keyring@0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@tallyho/hd-keyring/-/hd-keyring-0.4.0.tgz#0b78b5f60aa625862a8c54b136a3c934fe67d9df"
integrity sha512-IEJHyXAN3XSc3wUy6uEF6MiEpK3wjM63pMSS9uD/uG3c6zWpcGVJz7LxnIBSXo2KBiSXKY5Rr/nMud5PcrcddA==
"@tallyho/hd-keyring@0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@tallyho/hd-keyring/-/hd-keyring-0.5.0.tgz#e65b54fa3b5e0ef2020440d4b87b0f0a4e481bf0"
integrity sha512-AmqDNuFYzbYH78slTi8EykmBJd/2dhXDT0VWS/x6dsiR6XaAhuYR17YW/oyseAsTRC0OSdjUs8APxs4BGhLdfw==
dependencies:
"@ethersproject/abstract-provider" "5.6.1"
"@ethersproject/abstract-signer" "5.6.1"
Expand Down

0 comments on commit 0b885b7

Please sign in to comment.