Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: change password flow #1466

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
114 changes: 83 additions & 31 deletions apps/browser-extension-wallet/src/hooks/useWalletManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ export interface CreateWalletParams {
mnemonic: string[];
chainId?: Wallet.Cardano.ChainId;
}
export interface CreateWalletFromPrivateKeyParams {
name: string;
rootPrivateKeyBytes: HexBlob;
extendedAccountPublicKey: Wallet.Crypto.Bip32PublicKeyHex;
}

export type CreateWalletParamsBase = {
name: string;
chainId?: Wallet.Cardano.ChainId;
passphrase?: Buffer | undefined;
metadata: Wallet.WalletMetadata;
encryptedSecrets: { keyMaterial: HexBlob; rootPrivateKeyBytes: HexBlob };
extendedAccountPublicKey: Wallet.Crypto.Bip32PublicKeyHex;
};

interface CreateSharedWalletParams {
name: string;
Expand Down Expand Up @@ -105,6 +119,7 @@ export interface UseWalletManager {
activeWalletProps: WalletManagerActivateProps | null
) => Promise<Wallet.CardanoWallet | null>;
createWallet: (args: CreateWalletParams) => Promise<Wallet.CardanoWallet>;
createWalletFromPrivateKey: (args: CreateWalletFromPrivateKeyParams) => Promise<Wallet.CardanoWallet>;
createInMemorySharedWallet: (args: CreateSharedWalletParams) => Promise<Wallet.CardanoWallet>;
activateWallet: (args: Omit<WalletManagerActivateProps, 'chainId'>) => Promise<void>;
createHardwareWallet: (args: CreateHardwareWallet) => Promise<Wallet.CardanoWallet>;
Expand Down Expand Up @@ -487,40 +502,23 @@ export const useWalletManager = (): UseWalletManager => {
* Creates or restores a new in-memory wallet with the cardano-js-sdk and saves it in wallet repository
*/
const createWallet = useCallback(
async ({ mnemonic, name, chainId = getCurrentChainId() }: CreateWalletParams): Promise<Wallet.CardanoWallet> => {
async ({
name,
chainId = getCurrentChainId(),
passphrase,
metadata,
encryptedSecrets,
extendedAccountPublicKey
}: CreateWalletParamsBase): Promise<Wallet.CardanoWallet> => {
const accountIndex = 0;
const passphrase = Buffer.from(password.value, 'utf8');
const keyAgent = await Wallet.KeyManagement.InMemoryKeyAgent.fromBip39MnemonicWords(
{
chainId,
getPassphrase: async () => passphrase,
mnemonicWords: mnemonic,
accountIndex,
purpose: KeyManagement.KeyPurpose.STANDARD
},
{
bip32Ed25519: Wallet.bip32Ed25519,
logger
}
);

const lockValue = HexBlob.fromBytes(await Wallet.KeyManagement.emip3encrypt(LOCK_VALUE, passphrase));
const addWalletProps: AddWalletProps<Wallet.WalletMetadata, Wallet.AccountMetadata> = {
metadata: { name, lockValue, lastActiveAccountIndex: accountIndex },
encryptedSecrets: {
keyMaterial: await encryptMnemonic(mnemonic, passphrase),
rootPrivateKeyBytes: HexBlob.fromBytes(
Buffer.from(
(keyAgent.serializableData as Wallet.KeyManagement.SerializableInMemoryKeyAgentData)
.encryptedRootPrivateKeyBytes
)
)
},
metadata,
encryptedSecrets,
accounts: [
{
accountIndex,
metadata: { name: defaultAccountName(accountIndex) },
extendedAccountPublicKey: keyAgent.extendedAccountPublicKey
extendedAccountPublicKey
}
],
type: WalletType.InMemory
Expand All @@ -537,7 +535,7 @@ export const useWalletManager = (): UseWalletManager => {
saveValueInLocalStorage({ key: 'wallet', value: { name } });

// Clear passphrase
passphrase.fill(0);
if (passphrase) passphrase.fill(0);
clearSecrets();

return {
Expand All @@ -553,7 +551,60 @@ export const useWalletManager = (): UseWalletManager => {
}
};
},
[getCurrentChainId, password, clearSecrets]
[getCurrentChainId, clearSecrets]
);

const createWalletFromPrivateKey = useCallback(
async ({ name, rootPrivateKeyBytes, extendedAccountPublicKey }: CreateWalletFromPrivateKeyParams) =>
createWallet({
name,
metadata: { name, lastActiveAccountIndex: 0 },
encryptedSecrets: {
keyMaterial: HexBlob.fromBytes(Buffer.from('')),
rootPrivateKeyBytes
},
extendedAccountPublicKey
}),
[createWallet]
);

const createWalletFromMnemonic = useCallback(
async ({ name, chainId = getCurrentChainId(), mnemonic }: CreateWalletParams): Promise<Wallet.CardanoWallet> => {
const accountIndex = 0;
const passphrase = Buffer.from(password.value, 'utf8');
const lockValue = HexBlob.fromBytes(await Wallet.KeyManagement.emip3encrypt(LOCK_VALUE, passphrase));
const keyAgent = await Wallet.KeyManagement.InMemoryKeyAgent.fromBip39MnemonicWords(
{
chainId,
getPassphrase: async () => passphrase,
mnemonicWords: mnemonic,
accountIndex,
purpose: KeyManagement.KeyPurpose.STANDARD
},
{
bip32Ed25519: Wallet.bip32Ed25519,
logger
}
);
const metadata = {
name,
lastActiveAccountIndex: accountIndex,
lockValue
};
const encryptedSecrets = {
keyMaterial: await encryptMnemonic(mnemonic, passphrase),
rootPrivateKeyBytes: HexBlob.fromBytes(
Buffer.from(
(keyAgent.serializableData as Wallet.KeyManagement.SerializableInMemoryKeyAgentData)
.encryptedRootPrivateKeyBytes
)
)
};
const extendedAccountPublicKey = keyAgent.extendedAccountPublicKey;

return createWallet({ name, passphrase, metadata, encryptedSecrets, extendedAccountPublicKey });
},
[createWallet, getCurrentChainId, password?.value]
);

const activateWallet = useCallback(
Expand Down Expand Up @@ -935,7 +986,8 @@ export const useWalletManager = (): UseWalletManager => {
lockWallet,
unlockWallet,
loadWallet,
createWallet,
createWallet: createWalletFromMnemonic,
createWalletFromPrivateKey,
createInMemorySharedWallet,
createHardwareWallet,
createHardwareWalletRevamped,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const NamiView = withDappContext((): React.ReactElement => {
const [namiMigration, setNamiMigration] = useState<BackgroundStorage['namiMigration']>();
const backgroundServices = useBackgroundServiceAPIContext();
const {
createWallet,
createWalletFromPrivateKey,
getMnemonic,
deleteWallet,
switchNetwork,
Expand Down Expand Up @@ -161,7 +161,7 @@ export const NamiView = withDappContext((): React.ReactElement => {
isAnalyticsOptIn,
handleAnalyticsChoice,
sendEventToPostHog,
createWallet,
createWallet: createWalletFromPrivateKey,
getMnemonic,
deleteWallet,
fiatCurrency: fiatCurrency.code,
Expand Down
30 changes: 22 additions & 8 deletions packages/nami/src/adapters/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,36 @@ export const useChangePassword = ({
return useCallback(
async (currentPassword: string, newPassword: string) => {
try {
if (!wallet?.metadata?.name) {
if (
!wallet?.metadata?.name ||
!('encryptedSecrets' in wallet) ||
!('accounts' in wallet)
) {
return;
}
const mnemonic = await getMnemonic(Buffer.from(currentPassword));

const decryptedRootPrivateKeyBytes =
await Wallet.KeyManagement.emip3decrypt(
Buffer.from(wallet.encryptedSecrets.rootPrivateKeyBytes, 'hex'),
Buffer.from(currentPassword, 'utf8'),
);

const encryptedRootPrivateKeyBytes =
await Wallet.KeyManagement.emip3encrypt(
decryptedRootPrivateKeyBytes,
Buffer.from(newPassword, 'utf8'),
);

await deleteWallet(false);
const newWallet = await createWallet({
mnemonic,
name: wallet.metadata.name,
password: newPassword,
rootPrivateKeyBytes: Wallet.HexBlob.fromBytes(
encryptedRootPrivateKeyBytes,
),
extendedAccountPublicKey: wallet.accounts[0].extendedAccountPublicKey,
});
const { walletId } = newWallet.source.wallet;

if (!('accounts' in wallet)) {
return;
}

for await (const account of wallet.accounts) {
const { accountIndex, metadata, extendedAccountPublicKey } = account;
await (accountIndex === 0
Expand Down
5 changes: 3 additions & 2 deletions packages/nami/src/types/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { HexBlob } from '@cardano-sdk/util';
import type { Wallet } from '@lace/cardano';

export interface CreateWalletParams {
name: string;
mnemonic: string[];
password: string;
chainId?: Wallet.Cardano.ChainId;
rootPrivateKeyBytes?: HexBlob;
extendedAccountPublicKey?: Wallet.Crypto.Bip32PublicKeyHex;
}
Loading