Skip to content

Commit

Permalink
feat: derive default shielded keys from private key import
Browse files Browse the repository at this point in the history
  • Loading branch information
jurevans committed Nov 5, 2024
1 parent 0376020 commit b6df702
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 31 deletions.
2 changes: 1 addition & 1 deletion apps/extension/src/App/Accounts/ViewingKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const ViewingKey = (): JSX.Element => {
<PageHeader title="Viewing Key" />
<p className="text-white">
Your viewing key grants the holder access to all your balances and
transadction data. Please keep it secure to protect your data.
transaction data. Please keep it secure to protect your data.
</p>
<Input
label="Viewing Key"
Expand Down
7 changes: 3 additions & 4 deletions apps/extension/src/Setup/Common/Completion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,15 @@ export const Completion: React.FC<Props> = (props) => {

// Do not derive shielded if this is an imported private key, and
// ignore accounts with a non-zero 'change' path component:
if (accountSecret.t !== "PrivateKey" && path.change === 0) {
if (path.change === 0) {
setStatusInfo("Generating Shielded Account");
const shieldedAccount = await requester.sendMessage<DeriveAccountMsg>(
Ports.Background,
// If this is a default path, don't use zip32 index
// TODO: Should we include index of 0 on default path?
new DeriveAccountMsg(
path,
AccountType.ShieldedKeys,
storedAccount.alias
storedAccount.alias,
storedAccount.type
)
);
setShieldedAccountAddress(shieldedAccount.address);
Expand Down
4 changes: 2 additions & 2 deletions apps/extension/src/background/keyring/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ const handleDeriveAccountMsg: (
service: KeyRingService
) => InternalHandler<DeriveAccountMsg> = (service) => {
return async (_, msg) => {
const { path, accountType, alias } = msg;
return await service.deriveAccount(path, accountType, alias);
const { path, accountType, alias, parentType } = msg;
return await service.deriveAccount(path, accountType, alias, parentType);
};
};

Expand Down
58 changes: 37 additions & 21 deletions apps/extension/src/background/keyring/keyring.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PhraseSize } from "@namada/sdk/web";
import { PhraseSize, ShieldedKeys } from "@namada/sdk/web";
import { KVStore } from "@namada/storage";
import {
AccountType,
Expand All @@ -21,6 +21,7 @@ import {
UtilityStore,
} from "./types";

import { fromHex } from "@cosmjs/encoding";
import { SdkService } from "background/sdk";
import { VaultService } from "background/vault";
import { KeyStore, KeyStoreType, SensitiveType, VaultStorage } from "storage";
Expand Down Expand Up @@ -255,9 +256,10 @@ export class KeyRing {
}

public deriveShieldedAccount(
seed: Uint8Array,
secret: Uint8Array,
bip44Path: Bip44Path,
parentId: string
parentId: string,
parentType = AccountType.Mnemonic
): DerivedAccountInfo {
const storedPath = makeStoredPath(AccountType.ShieldedKeys, bip44Path);
const id = generateId(
Expand All @@ -273,13 +275,22 @@ export class KeyRing {
storedPath.index || "none"
);
const keysNs = this.sdkService.getSdk().getKeys();
const { address, viewingKey, spendingKey } = keysNs.deriveShieldedFromSeed(
seed,
bip44Path,
// Derives default shielded keys account from bip44Path.account
// TODO: Expose function to accept { account, index }
{ account: bip44Path.account }
);

let shieldedKeys: ShieldedKeys;

if (parentType === AccountType.Mnemonic) {
shieldedKeys = keysNs.deriveShieldedFromSeed(secret, bip44Path, {
account: bip44Path.account,
});
} else if (parentType === AccountType.PrivateKey) {
shieldedKeys = keysNs.deriveShieldedFromPrivateKey(secret, {
account: bip44Path.account,
});
} else {
throw new Error(`Invalid account type! ${parentType}`);
}

const { address, viewingKey, spendingKey } = shieldedKeys;

return {
address,
Expand All @@ -289,27 +300,27 @@ export class KeyRing {
};
}

private async getParentSeed(): Promise<{
private async getParentSecret(parentType = AccountType.Mnemonic): Promise<{
parentId: string;
seed: Uint8Array;
secret: Uint8Array;
}> {
const activeAccount = await this.getActiveAccount();

if (!activeAccount) {
throw "No active account has been found";
}

const storedMnemonic = await this.vaultStorage.findOneOrFail(
const storedSecret = await this.vaultStorage.findOneOrFail(
KeyStore,
"id",
activeAccount.id
);

const parentId = storedMnemonic.public.id;
const parentId = storedSecret.public.id;
try {
const sensitiveData =
await this.vaultService.reveal<SensitiveAccountStoreData>(
storedMnemonic.sensitive
storedSecret.sensitive
);

if (!sensitiveData) {
Expand All @@ -320,10 +331,14 @@ export class KeyRing {

const { text, passphrase } = sensitiveData;

const mnemonicSdk = this.sdkService.getSdk().getMnemonic();
const seed = mnemonicSdk.toSeed(text, passphrase);
if (parentType === AccountType.Mnemonic) {
const mnemonicSdk = this.sdkService.getSdk().getMnemonic();
const seed = mnemonicSdk.toSeed(text, passphrase);

return { parentId, seed };
return { parentId, secret: seed };
} else {
return { parentId, secret: fromHex(text) };
}
} catch (e) {
console.error(e);
throw Error("Could not decrypt mnemonic using the provided password");
Expand Down Expand Up @@ -364,7 +379,8 @@ export class KeyRing {
public async deriveAccount(
bip44Path: Bip44Path,
type: AccountType,
alias: string
alias: string,
parentType?: AccountType
): Promise<DerivedAccount> {
await this.vaultService.assertIsUnlocked();
// Prepare path for either BIP44 or ZIP32 stored value
Expand All @@ -379,9 +395,9 @@ export class KeyRing {
this.deriveTransparentAccount
: this.deriveShieldedAccount).bind(this);

const { seed, parentId } = await this.getParentSeed();
const { secret, parentId } = await this.getParentSecret(parentType);

const info = deriveFn(seed, bip44Path, parentId);
const info = deriveFn(secret, bip44Path, parentId, parentType);

// Check whether keys already exist for this account
const existingAccount = await this.queryAccountByAddress(info.address);
Expand Down
3 changes: 2 additions & 1 deletion apps/extension/src/background/keyring/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ export class DeriveAccountMsg extends Message<DerivedAccount> {
constructor(
public readonly path: Bip44Path,
public readonly accountType: AccountType,
public readonly alias: string
public readonly alias: string,
public readonly parentType?: AccountType
) {
super();
}
Expand Down
10 changes: 8 additions & 2 deletions apps/extension/src/background/keyring/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,15 @@ export class KeyRingService {
async deriveAccount(
path: Bip44Path,
type: AccountType,
alias: string
alias: string,
parentType?: AccountType
): Promise<DerivedAccount> {
const account = await this._keyRing.deriveAccount(path, type, alias);
const account = await this._keyRing.deriveAccount(
path,
type,
alias,
parentType
);
await this.broadcaster.updateAccounts();
return account;
}
Expand Down

0 comments on commit b6df702

Please sign in to comment.