mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
Pull out Secp256k1Wallet into separate module
This commit is contained in:
parent
91aec70650
commit
0ea2babd74
@ -6,6 +6,7 @@ import { CosmosClient, isPostTxFailure } from "./cosmosclient";
|
||||
import { makeSignBytes } from "./encoding";
|
||||
import { LcdClient } from "./lcdapi";
|
||||
import { isMsgSend, MsgSend } from "./msgs";
|
||||
import { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
import { SigningCosmosClient } from "./signingcosmosclient";
|
||||
import {
|
||||
faucet,
|
||||
@ -16,7 +17,6 @@ import {
|
||||
wasmdEnabled,
|
||||
} from "./testutils.spec";
|
||||
import { CosmosSdkTx } from "./types";
|
||||
import { Secp256k1Wallet } from "./wallet";
|
||||
|
||||
interface TestTxSend {
|
||||
readonly sender: string;
|
||||
|
@ -6,6 +6,7 @@ import { assertIsPostTxSuccess, CosmosClient, PrivateCosmWasmClient } from "./co
|
||||
import { makeSignBytes } from "./encoding";
|
||||
import { findAttribute } from "./logs";
|
||||
import { MsgSend } from "./msgs";
|
||||
import { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
import cosmoshub from "./testdata/cosmoshub.json";
|
||||
import {
|
||||
faucet,
|
||||
@ -16,7 +17,6 @@ import {
|
||||
wasmd,
|
||||
} from "./testutils.spec";
|
||||
import { StdFee } from "./types";
|
||||
import { Secp256k1Wallet } from "./wallet";
|
||||
|
||||
const blockTime = 1_000; // ms
|
||||
|
||||
|
@ -85,4 +85,5 @@ export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
|
||||
export { OfflineSigner, Secp256k1Wallet, makeCosmoshubPath } from "./wallet";
|
||||
export { OfflineSigner, makeCosmoshubPath } from "./wallet";
|
||||
export { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
|
@ -6,6 +6,7 @@ import { coin, coins } from "../coins";
|
||||
import { assertIsPostTxSuccess } from "../cosmosclient";
|
||||
import { makeSignBytes } from "../encoding";
|
||||
import { MsgDelegate } from "../msgs";
|
||||
import { Secp256k1Wallet } from "../secp256k1wallet";
|
||||
import { SigningCosmosClient } from "../signingcosmosclient";
|
||||
import {
|
||||
bigDecimalMatcher,
|
||||
@ -16,7 +17,6 @@ import {
|
||||
wasmd,
|
||||
wasmdEnabled,
|
||||
} from "../testutils.spec";
|
||||
import { Secp256k1Wallet } from "../wallet";
|
||||
import { DistributionExtension, setupDistributionExtension } from "./distribution";
|
||||
import { LcdClient } from "./lcdclient";
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { sleep } from "@cosmjs/utils";
|
||||
import { coins } from "../coins";
|
||||
import { assertIsPostTxSuccess } from "../cosmosclient";
|
||||
import { makeSignBytes } from "../encoding";
|
||||
import { Secp256k1Wallet } from "../secp256k1wallet";
|
||||
import { SigningCosmosClient } from "../signingcosmosclient";
|
||||
import {
|
||||
dateTimeStampMatcher,
|
||||
@ -13,7 +14,6 @@ import {
|
||||
wasmd,
|
||||
wasmdEnabled,
|
||||
} from "../testutils.spec";
|
||||
import { Secp256k1Wallet } from "../wallet";
|
||||
import { GovExtension, GovParametersType, setupGovExtension } from "./gov";
|
||||
import { LcdClient } from "./lcdclient";
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { isPostTxFailure } from "../cosmosclient";
|
||||
import { makeSignBytes } from "../encoding";
|
||||
import { parseLogs } from "../logs";
|
||||
import { MsgSend } from "../msgs";
|
||||
import { Secp256k1Wallet } from "../secp256k1wallet";
|
||||
import { SigningCosmosClient } from "../signingcosmosclient";
|
||||
import cosmoshub from "../testdata/cosmoshub.json";
|
||||
import {
|
||||
@ -19,7 +20,7 @@ import {
|
||||
wasmdEnabled,
|
||||
} from "../testutils.spec";
|
||||
import { StdFee } from "../types";
|
||||
import { makeCosmoshubPath, Secp256k1Wallet } from "../wallet";
|
||||
import { makeCosmoshubPath } from "../wallet";
|
||||
import { setupAuthExtension } from "./auth";
|
||||
import { TxsResponse } from "./base";
|
||||
import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient";
|
||||
|
@ -5,6 +5,7 @@ import { coin, coins } from "../coins";
|
||||
import { assertIsPostTxSuccess } from "../cosmosclient";
|
||||
import { makeSignBytes } from "../encoding";
|
||||
import { MsgDelegate, MsgUndelegate } from "../msgs";
|
||||
import { Secp256k1Wallet } from "../secp256k1wallet";
|
||||
import { SigningCosmosClient } from "../signingcosmosclient";
|
||||
import {
|
||||
bigDecimalMatcher,
|
||||
@ -16,7 +17,6 @@ import {
|
||||
wasmd,
|
||||
wasmdEnabled,
|
||||
} from "../testutils.spec";
|
||||
import { Secp256k1Wallet } from "../wallet";
|
||||
import { LcdClient } from "./lcdclient";
|
||||
import { BondStatus, setupStakingExtension, StakingExtension } from "./staking";
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, toAscii } from "@cosmjs/encoding";
|
||||
|
||||
import { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet";
|
||||
import { base64Matcher, hexMatcher } from "./testutils.spec";
|
||||
import { executeKdf, extractKdfConfiguration, KdfConfiguration, Secp256k1Wallet } from "./wallet";
|
||||
import { executeKdf, KdfConfiguration } from "./wallet";
|
||||
|
||||
describe("Secp256k1Wallet", () => {
|
||||
// m/44'/118'/0'/0/0
|
332
packages/sdk38/src/secp256k1wallet.ts
Normal file
332
packages/sdk38/src/secp256k1wallet.ts
Normal file
@ -0,0 +1,332 @@
|
||||
import {
|
||||
Argon2id,
|
||||
Bip39,
|
||||
EnglishMnemonic,
|
||||
pathToString,
|
||||
Random,
|
||||
Secp256k1,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
Slip10RawIndex,
|
||||
stringToPath,
|
||||
xchacha20NonceLength,
|
||||
Xchacha20poly1305Ietf,
|
||||
} from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, fromUtf8, toAscii, toBase64, toHex, toUtf8 } from "@cosmjs/encoding";
|
||||
import { assert, isNonNullObject } from "@cosmjs/utils";
|
||||
|
||||
import { rawSecp256k1PubkeyToAddress } from "./address";
|
||||
import { encodeSecp256k1Signature } from "./signature";
|
||||
import { StdSignature } from "./types";
|
||||
import {
|
||||
AccountData,
|
||||
Algo,
|
||||
executeKdf,
|
||||
KdfConfiguration,
|
||||
makeCosmoshubPath,
|
||||
OfflineSigner,
|
||||
prehash,
|
||||
PrehashType,
|
||||
} from "./wallet";
|
||||
|
||||
const serializationTypeV1 = "secp256k1wallet-v1";
|
||||
|
||||
/**
|
||||
* A fixed salt is chosen to archive a deterministic password to key derivation.
|
||||
* This reduces the scope of a potential rainbow attack to all CosmJS users.
|
||||
* Must be 16 bytes due to implementation limitations.
|
||||
*/
|
||||
const cosmjsSalt = toAscii("The CosmJS salt.");
|
||||
|
||||
/**
|
||||
* A KDF configuration that is not very strong but can be used on the main thread.
|
||||
* It takes about 1 second in Node.js 12.15 and should have similar runtimes in other modern Wasm hosts.
|
||||
*/
|
||||
const basicPasswordHashingOptions: KdfConfiguration = {
|
||||
algorithm: "argon2id",
|
||||
params: {
|
||||
outputLength: 32,
|
||||
opsLimit: 20,
|
||||
memLimitKib: 12 * 1024,
|
||||
},
|
||||
};
|
||||
|
||||
const algorithmIdXchacha20poly1305Ietf = "xchacha20poly1305-ietf";
|
||||
|
||||
/**
|
||||
* This interface describes a JSON object holding the encrypted wallet and the meta data.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletSerialization {
|
||||
/** A format+version identifier for this serialization format */
|
||||
readonly type: string;
|
||||
/** Information about the key derivation function (i.e. password to encrytion key) */
|
||||
readonly kdf: KdfConfiguration;
|
||||
/** Information about the symmetric encryption */
|
||||
readonly encryption: {
|
||||
/**
|
||||
* An algorithm identifier, such as "xchacha20poly1305-ietf".
|
||||
*/
|
||||
readonly algorithm: string;
|
||||
/** A map of algorithm-specific parameters */
|
||||
readonly params: Record<string, unknown>;
|
||||
};
|
||||
/** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */
|
||||
readonly data: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The data of a wallet serialization that is encrypted.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletData {
|
||||
readonly mnemonic: string;
|
||||
readonly accounts: ReadonlyArray<{
|
||||
readonly algo: string;
|
||||
readonly hdPath: string;
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
function extractKdfConfigurationV1(document: any): KdfConfiguration {
|
||||
return document.kdf;
|
||||
}
|
||||
|
||||
export function extractKdfConfiguration(serialization: string): KdfConfiguration {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
|
||||
switch ((root as any).type) {
|
||||
case serializationTypeV1:
|
||||
return extractKdfConfigurationV1(root);
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
export class Secp256k1Wallet implements OfflineSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
* @param mnemonic Any valid English mnemonic.
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
public static async fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
|
||||
prefix = "cosmos",
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const mnemonicChecked = new EnglishMnemonic(mnemonic);
|
||||
const seed = await Bip39.mnemonicToSeed(mnemonicChecked);
|
||||
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return new Secp256k1Wallet(
|
||||
mnemonicChecked,
|
||||
hdPath,
|
||||
privkey,
|
||||
Secp256k1.compressPubkey(uncompressed),
|
||||
prefix,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new wallet with a BIP39 mnemonic of the given length.
|
||||
*
|
||||
* @param length The number of words in the mnemonic (12, 15, 18, 21 or 24).
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
public static async generate(
|
||||
length: 12 | 15 | 18 | 21 | 24 = 12,
|
||||
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
|
||||
prefix = "cosmos",
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const entropyLength = 4 * Math.floor((11 * length) / 33);
|
||||
const entropy = Random.getBytes(entropyLength);
|
||||
const mnemonic = Bip39.encode(entropy);
|
||||
return Secp256k1Wallet.fromMnemonic(mnemonic.toString(), hdPath, prefix);
|
||||
}
|
||||
|
||||
public static async deserialize(serialization: string, password: string): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
switch ((root as any).type) {
|
||||
case serializationTypeV1:
|
||||
return Secp256k1Wallet.deserializeType1(serialization, password);
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
public static async deserializeWithEncryptionKey(
|
||||
serialization: string,
|
||||
encryptionKey: Uint8Array,
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
const untypedRoot: any = root;
|
||||
switch (untypedRoot.type) {
|
||||
case serializationTypeV1: {
|
||||
const nonce = fromHex(untypedRoot.encryption.params.nonce);
|
||||
const decryptedBytes = await Xchacha20poly1305Ietf.decrypt(
|
||||
fromBase64(untypedRoot.data),
|
||||
encryptionKey,
|
||||
nonce,
|
||||
);
|
||||
const decryptedDocument = JSON.parse(fromUtf8(decryptedBytes));
|
||||
const { mnemonic, accounts } = decryptedDocument;
|
||||
assert(typeof mnemonic === "string");
|
||||
if (!Array.isArray(accounts)) throw new Error("Property 'accounts' is not an array");
|
||||
if (accounts.length !== 1) throw new Error("Property 'accounts' only supports one entry");
|
||||
const account = accounts[0];
|
||||
if (!isNonNullObject(account)) throw new Error("Account is not an object.");
|
||||
const { algo, hdPath, prefix } = account as any;
|
||||
assert(algo === "secp256k1");
|
||||
assert(typeof hdPath === "string");
|
||||
assert(typeof prefix === "string");
|
||||
|
||||
return Secp256k1Wallet.fromMnemonic(mnemonic, stringToPath(hdPath), prefix);
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
private static async deserializeType1(serialization: string, password: string): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
const untypedRoot: any = root;
|
||||
switch (untypedRoot.type) {
|
||||
case serializationTypeV1: {
|
||||
let encryptionKey: Uint8Array;
|
||||
switch (untypedRoot.kdf.algorithm) {
|
||||
case "argon2id": {
|
||||
const kdfOptions = untypedRoot.kdf.params;
|
||||
encryptionKey = await Argon2id.execute(password, cosmjsSalt, kdfOptions);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported KDF algorithm");
|
||||
}
|
||||
return Secp256k1Wallet.deserializeWithEncryptionKey(serialization, encryptionKey);
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
/** Base secret */
|
||||
private readonly secret: EnglishMnemonic;
|
||||
/** Derivation instrations */
|
||||
private readonly accounts: ReadonlyArray<{
|
||||
readonly algo: Algo;
|
||||
readonly hdPath: readonly Slip10RawIndex[];
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
/** Derived data */
|
||||
private readonly pubkey: Uint8Array;
|
||||
private readonly privkey: Uint8Array;
|
||||
|
||||
private constructor(
|
||||
mnemonic: EnglishMnemonic,
|
||||
hdPath: readonly Slip10RawIndex[],
|
||||
privkey: Uint8Array,
|
||||
pubkey: Uint8Array,
|
||||
prefix: string,
|
||||
) {
|
||||
this.secret = mnemonic;
|
||||
this.accounts = [
|
||||
{
|
||||
algo: "secp256k1",
|
||||
hdPath: hdPath,
|
||||
prefix: prefix,
|
||||
},
|
||||
];
|
||||
this.privkey = privkey;
|
||||
this.pubkey = pubkey;
|
||||
}
|
||||
|
||||
public get mnemonic(): string {
|
||||
return this.secret.toString();
|
||||
}
|
||||
|
||||
private get address(): string {
|
||||
return rawSecp256k1PubkeyToAddress(this.pubkey, this.accounts[0].prefix);
|
||||
}
|
||||
|
||||
public async getAccounts(): Promise<readonly AccountData[]> {
|
||||
return [
|
||||
{
|
||||
address: this.address,
|
||||
algo: this.accounts[0].algo,
|
||||
pubkey: this.pubkey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public async sign(
|
||||
address: string,
|
||||
message: Uint8Array,
|
||||
prehashType: PrehashType = "sha256",
|
||||
): Promise<StdSignature> {
|
||||
if (address !== this.address) {
|
||||
throw new Error(`Address ${address} not found in wallet`);
|
||||
}
|
||||
const hashedMessage = prehash(message, prehashType);
|
||||
const signature = await Secp256k1.createSignature(hashedMessage, this.privkey);
|
||||
const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]);
|
||||
return encodeSecp256k1Signature(this.pubkey, signatureBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* @param password The user provided password used to generate an encryption key via a KDF.
|
||||
* This is not normalized internally (see "Unicode normalization" to learn more).
|
||||
*/
|
||||
public async serialize(password: string): Promise<string> {
|
||||
const kdfConfiguration = basicPasswordHashingOptions;
|
||||
const encryptionKey = await executeKdf(password, kdfConfiguration);
|
||||
return this.serializeWithEncryptionKey(encryptionKey, kdfConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* This is an advanced alternative of calling `serialize(password)` directly, which allows you to
|
||||
* offload the KDF execution to an non-UI thread (e.g. in a WebWorker).
|
||||
*
|
||||
* The caller is responsible for ensuring the key was derived with the given kdf options. If this
|
||||
* is not the case, the wallet cannot be restored with the original password.
|
||||
*/
|
||||
public async serializeWithEncryptionKey(
|
||||
encryptionKey: Uint8Array,
|
||||
kdfConfiguration: KdfConfiguration,
|
||||
): Promise<string> {
|
||||
const encrytedData: Secp256k1WalletData = {
|
||||
mnemonic: this.mnemonic,
|
||||
accounts: this.accounts.map((account) => ({
|
||||
algo: account.algo,
|
||||
hdPath: pathToString(account.hdPath),
|
||||
prefix: account.prefix,
|
||||
})),
|
||||
};
|
||||
const message = toUtf8(JSON.stringify(encrytedData));
|
||||
const nonce = Random.getBytes(xchacha20NonceLength);
|
||||
const encrypted = await Xchacha20poly1305Ietf.encrypt(message, encryptionKey, nonce);
|
||||
|
||||
const out: Secp256k1WalletSerialization = {
|
||||
type: serializationTypeV1,
|
||||
kdf: kdfConfiguration,
|
||||
encryption: {
|
||||
algorithm: algorithmIdXchacha20poly1305Ietf,
|
||||
params: {
|
||||
nonce: toHex(nonce),
|
||||
},
|
||||
},
|
||||
data: toBase64(encrypted),
|
||||
};
|
||||
return JSON.stringify(out);
|
||||
}
|
||||
}
|
@ -4,9 +4,9 @@ import { assert } from "@cosmjs/utils";
|
||||
import { Coin, coin, coins } from "./coins";
|
||||
import { assertIsPostTxSuccess, PrivateCosmWasmClient } from "./cosmosclient";
|
||||
import { MsgDelegate } from "./msgs";
|
||||
import { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
import { SigningCosmosClient } from "./signingcosmosclient";
|
||||
import { makeRandomAddress, pendingWithoutWasmd, validatorAddress } from "./testutils.spec";
|
||||
import { Secp256k1Wallet } from "./wallet";
|
||||
|
||||
const httpUrl = "http://localhost:1317";
|
||||
|
||||
|
@ -1,25 +1,7 @@
|
||||
import {
|
||||
Argon2id,
|
||||
Argon2idOptions,
|
||||
Bip39,
|
||||
EnglishMnemonic,
|
||||
pathToString,
|
||||
Random,
|
||||
Secp256k1,
|
||||
Sha256,
|
||||
Sha512,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
Slip10RawIndex,
|
||||
stringToPath,
|
||||
xchacha20NonceLength,
|
||||
Xchacha20poly1305Ietf,
|
||||
} from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, fromUtf8, toAscii, toBase64, toHex, toUtf8 } from "@cosmjs/encoding";
|
||||
import { assert, isNonNullObject } from "@cosmjs/utils";
|
||||
import { Argon2id, Argon2idOptions, Sha256, Sha512, Slip10RawIndex } from "@cosmjs/crypto";
|
||||
import { toAscii } from "@cosmjs/encoding";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { rawSecp256k1PubkeyToAddress } from "./address";
|
||||
import { encodeSecp256k1Signature } from "./signature";
|
||||
import { StdSignature } from "./types";
|
||||
|
||||
export type PrehashType = "sha256" | "sha512" | null;
|
||||
@ -45,7 +27,7 @@ export interface OfflineSigner {
|
||||
readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
|
||||
}
|
||||
|
||||
function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array {
|
||||
export function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array {
|
||||
switch (type) {
|
||||
case null:
|
||||
return new Uint8Array([...bytes]);
|
||||
@ -72,8 +54,6 @@ export function makeCosmoshubPath(a: number): readonly Slip10RawIndex[] {
|
||||
];
|
||||
}
|
||||
|
||||
const serializationTypeV1 = "secp256k1wallet-v1";
|
||||
|
||||
/**
|
||||
* A fixed salt is chosen to archive a deterministic password to key derivation.
|
||||
* This reduces the scope of a potential rainbow attack to all CosmJS users.
|
||||
@ -81,72 +61,6 @@ const serializationTypeV1 = "secp256k1wallet-v1";
|
||||
*/
|
||||
const cosmjsSalt = toAscii("The CosmJS salt.");
|
||||
|
||||
/**
|
||||
* A KDF configuration that is not very strong but can be used on the main thread.
|
||||
* It takes about 1 second in Node.js 12.15 and should have similar runtimes in other modern Wasm hosts.
|
||||
*/
|
||||
const basicPasswordHashingOptions: KdfConfiguration = {
|
||||
algorithm: "argon2id",
|
||||
params: {
|
||||
outputLength: 32,
|
||||
opsLimit: 20,
|
||||
memLimitKib: 12 * 1024,
|
||||
},
|
||||
};
|
||||
|
||||
const algorithmIdXchacha20poly1305Ietf = "xchacha20poly1305-ietf";
|
||||
|
||||
/**
|
||||
* This interface describes a JSON object holding the encrypted wallet and the meta data.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletSerialization {
|
||||
/** A format+version identifier for this serialization format */
|
||||
readonly type: string;
|
||||
/** Information about the key derivation function (i.e. password to encrytion key) */
|
||||
readonly kdf: KdfConfiguration;
|
||||
/** Information about the symmetric encryption */
|
||||
readonly encryption: {
|
||||
/**
|
||||
* An algorithm identifier, such as "xchacha20poly1305-ietf".
|
||||
*/
|
||||
readonly algorithm: string;
|
||||
/** A map of algorithm-specific parameters */
|
||||
readonly params: Record<string, unknown>;
|
||||
};
|
||||
/** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */
|
||||
readonly data: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The data of a wallet serialization that is encrypted.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletData {
|
||||
readonly mnemonic: string;
|
||||
readonly accounts: ReadonlyArray<{
|
||||
readonly algo: string;
|
||||
readonly hdPath: string;
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
function extractKdfConfigurationV1(document: any): KdfConfiguration {
|
||||
return document.kdf;
|
||||
}
|
||||
|
||||
export function extractKdfConfiguration(serialization: string): KdfConfiguration {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
|
||||
switch ((root as any).type) {
|
||||
case serializationTypeV1:
|
||||
return extractKdfConfigurationV1(root);
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
export interface KdfConfiguration {
|
||||
/**
|
||||
* An algorithm identifier, such as "argon2id" or "scrypt".
|
||||
@ -170,230 +84,3 @@ export async function executeKdf(password: string, configuration: KdfConfigurati
|
||||
throw new Error("Unsupported KDF algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
export class Secp256k1Wallet implements OfflineSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
* @param mnemonic Any valid English mnemonic.
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
public static async fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
|
||||
prefix = "cosmos",
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const mnemonicChecked = new EnglishMnemonic(mnemonic);
|
||||
const seed = await Bip39.mnemonicToSeed(mnemonicChecked);
|
||||
const { privkey } = Slip10.derivePath(Slip10Curve.Secp256k1, seed, hdPath);
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return new Secp256k1Wallet(
|
||||
mnemonicChecked,
|
||||
hdPath,
|
||||
privkey,
|
||||
Secp256k1.compressPubkey(uncompressed),
|
||||
prefix,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new wallet with a BIP39 mnemonic of the given length.
|
||||
*
|
||||
* @param length The number of words in the mnemonic (12, 15, 18, 21 or 24).
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
public static async generate(
|
||||
length: 12 | 15 | 18 | 21 | 24 = 12,
|
||||
hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0),
|
||||
prefix = "cosmos",
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const entropyLength = 4 * Math.floor((11 * length) / 33);
|
||||
const entropy = Random.getBytes(entropyLength);
|
||||
const mnemonic = Bip39.encode(entropy);
|
||||
return Secp256k1Wallet.fromMnemonic(mnemonic.toString(), hdPath, prefix);
|
||||
}
|
||||
|
||||
public static async deserialize(serialization: string, password: string): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
switch ((root as any).type) {
|
||||
case serializationTypeV1:
|
||||
return Secp256k1Wallet.deserializeType1(serialization, password);
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
public static async deserializeWithEncryptionKey(
|
||||
serialization: string,
|
||||
encryptionKey: Uint8Array,
|
||||
): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
const untypedRoot: any = root;
|
||||
switch (untypedRoot.type) {
|
||||
case serializationTypeV1: {
|
||||
const nonce = fromHex(untypedRoot.encryption.params.nonce);
|
||||
const decryptedBytes = await Xchacha20poly1305Ietf.decrypt(
|
||||
fromBase64(untypedRoot.data),
|
||||
encryptionKey,
|
||||
nonce,
|
||||
);
|
||||
const decryptedDocument = JSON.parse(fromUtf8(decryptedBytes));
|
||||
const { mnemonic, accounts } = decryptedDocument;
|
||||
assert(typeof mnemonic === "string");
|
||||
if (!Array.isArray(accounts)) throw new Error("Property 'accounts' is not an array");
|
||||
if (accounts.length !== 1) throw new Error("Property 'accounts' only supports one entry");
|
||||
const account = accounts[0];
|
||||
if (!isNonNullObject(account)) throw new Error("Account is not an object.");
|
||||
const { algo, hdPath, prefix } = account as any;
|
||||
assert(algo === "secp256k1");
|
||||
assert(typeof hdPath === "string");
|
||||
assert(typeof prefix === "string");
|
||||
|
||||
return Secp256k1Wallet.fromMnemonic(mnemonic, stringToPath(hdPath), prefix);
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
private static async deserializeType1(serialization: string, password: string): Promise<Secp256k1Wallet> {
|
||||
const root = JSON.parse(serialization);
|
||||
if (!isNonNullObject(root)) throw new Error("Root document is not an object.");
|
||||
const untypedRoot: any = root;
|
||||
switch (untypedRoot.type) {
|
||||
case serializationTypeV1: {
|
||||
let encryptionKey: Uint8Array;
|
||||
switch (untypedRoot.kdf.algorithm) {
|
||||
case "argon2id": {
|
||||
const kdfOptions = untypedRoot.kdf.params;
|
||||
encryptionKey = await Argon2id.execute(password, cosmjsSalt, kdfOptions);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported KDF algorithm");
|
||||
}
|
||||
return Secp256k1Wallet.deserializeWithEncryptionKey(serialization, encryptionKey);
|
||||
}
|
||||
default:
|
||||
throw new Error("Unsupported serialization type");
|
||||
}
|
||||
}
|
||||
|
||||
/** Base secret */
|
||||
private readonly secret: EnglishMnemonic;
|
||||
/** Derivation instrations */
|
||||
private readonly accounts: ReadonlyArray<{
|
||||
readonly algo: Algo;
|
||||
readonly hdPath: readonly Slip10RawIndex[];
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
/** Derived data */
|
||||
private readonly pubkey: Uint8Array;
|
||||
private readonly privkey: Uint8Array;
|
||||
|
||||
private constructor(
|
||||
mnemonic: EnglishMnemonic,
|
||||
hdPath: readonly Slip10RawIndex[],
|
||||
privkey: Uint8Array,
|
||||
pubkey: Uint8Array,
|
||||
prefix: string,
|
||||
) {
|
||||
this.secret = mnemonic;
|
||||
this.accounts = [
|
||||
{
|
||||
algo: "secp256k1",
|
||||
hdPath: hdPath,
|
||||
prefix: prefix,
|
||||
},
|
||||
];
|
||||
this.privkey = privkey;
|
||||
this.pubkey = pubkey;
|
||||
}
|
||||
|
||||
public get mnemonic(): string {
|
||||
return this.secret.toString();
|
||||
}
|
||||
|
||||
private get address(): string {
|
||||
return rawSecp256k1PubkeyToAddress(this.pubkey, this.accounts[0].prefix);
|
||||
}
|
||||
|
||||
public async getAccounts(): Promise<readonly AccountData[]> {
|
||||
return [
|
||||
{
|
||||
address: this.address,
|
||||
algo: this.accounts[0].algo,
|
||||
pubkey: this.pubkey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public async sign(
|
||||
address: string,
|
||||
message: Uint8Array,
|
||||
prehashType: PrehashType = "sha256",
|
||||
): Promise<StdSignature> {
|
||||
if (address !== this.address) {
|
||||
throw new Error(`Address ${address} not found in wallet`);
|
||||
}
|
||||
const hashedMessage = prehash(message, prehashType);
|
||||
const signature = await Secp256k1.createSignature(hashedMessage, this.privkey);
|
||||
const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]);
|
||||
return encodeSecp256k1Signature(this.pubkey, signatureBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* @param password The user provided password used to generate an encryption key via a KDF.
|
||||
* This is not normalized internally (see "Unicode normalization" to learn more).
|
||||
*/
|
||||
public async serialize(password: string): Promise<string> {
|
||||
const kdfConfiguration = basicPasswordHashingOptions;
|
||||
const encryptionKey = await executeKdf(password, kdfConfiguration);
|
||||
return this.serializeWithEncryptionKey(encryptionKey, kdfConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* This is an advanced alternative of calling `serialize(password)` directly, which allows you to
|
||||
* offload the KDF execution to an non-UI thread (e.g. in a WebWorker).
|
||||
*
|
||||
* The caller is responsible for ensuring the key was derived with the given kdf options. If this
|
||||
* is not the case, the wallet cannot be restored with the original password.
|
||||
*/
|
||||
public async serializeWithEncryptionKey(
|
||||
encryptionKey: Uint8Array,
|
||||
kdfConfiguration: KdfConfiguration,
|
||||
): Promise<string> {
|
||||
const encrytedData: Secp256k1WalletData = {
|
||||
mnemonic: this.mnemonic,
|
||||
accounts: this.accounts.map((account) => ({
|
||||
algo: account.algo,
|
||||
hdPath: pathToString(account.hdPath),
|
||||
prefix: account.prefix,
|
||||
})),
|
||||
};
|
||||
const message = toUtf8(JSON.stringify(encrytedData));
|
||||
const nonce = Random.getBytes(xchacha20NonceLength);
|
||||
const encrypted = await Xchacha20poly1305Ietf.encrypt(message, encryptionKey, nonce);
|
||||
|
||||
const out: Secp256k1WalletSerialization = {
|
||||
type: serializationTypeV1,
|
||||
kdf: kdfConfiguration,
|
||||
encryption: {
|
||||
algorithm: algorithmIdXchacha20poly1305Ietf,
|
||||
params: {
|
||||
nonce: toHex(nonce),
|
||||
},
|
||||
},
|
||||
data: toBase64(encrypted),
|
||||
};
|
||||
return JSON.stringify(out);
|
||||
}
|
||||
}
|
||||
|
3
packages/sdk38/types/index.d.ts
vendored
3
packages/sdk38/types/index.d.ts
vendored
@ -83,4 +83,5 @@ export { findSequenceForSignedTx } from "./sequence";
|
||||
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
|
||||
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";
|
||||
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
|
||||
export { OfflineSigner, Secp256k1Wallet, makeCosmoshubPath } from "./wallet";
|
||||
export { OfflineSigner, makeCosmoshubPath } from "./wallet";
|
||||
export { Secp256k1Wallet } from "./secp256k1wallet";
|
||||
|
98
packages/sdk38/types/secp256k1wallet.d.ts
vendored
Normal file
98
packages/sdk38/types/secp256k1wallet.d.ts
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
import { Slip10RawIndex } from "@cosmjs/crypto";
|
||||
import { StdSignature } from "./types";
|
||||
import { AccountData, KdfConfiguration, OfflineSigner, PrehashType } from "./wallet";
|
||||
/**
|
||||
* This interface describes a JSON object holding the encrypted wallet and the meta data.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletSerialization {
|
||||
/** A format+version identifier for this serialization format */
|
||||
readonly type: string;
|
||||
/** Information about the key derivation function (i.e. password to encrytion key) */
|
||||
readonly kdf: KdfConfiguration;
|
||||
/** Information about the symmetric encryption */
|
||||
readonly encryption: {
|
||||
/**
|
||||
* An algorithm identifier, such as "xchacha20poly1305-ietf".
|
||||
*/
|
||||
readonly algorithm: string;
|
||||
/** A map of algorithm-specific parameters */
|
||||
readonly params: Record<string, unknown>;
|
||||
};
|
||||
/** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */
|
||||
readonly data: string;
|
||||
}
|
||||
/**
|
||||
* The data of a wallet serialization that is encrypted.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletData {
|
||||
readonly mnemonic: string;
|
||||
readonly accounts: ReadonlyArray<{
|
||||
readonly algo: string;
|
||||
readonly hdPath: string;
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
}
|
||||
export declare function extractKdfConfiguration(serialization: string): KdfConfiguration;
|
||||
export declare class Secp256k1Wallet implements OfflineSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
* @param mnemonic Any valid English mnemonic.
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
static fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath?: readonly Slip10RawIndex[],
|
||||
prefix?: string,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
/**
|
||||
* Generates a new wallet with a BIP39 mnemonic of the given length.
|
||||
*
|
||||
* @param length The number of words in the mnemonic (12, 15, 18, 21 or 24).
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
static generate(
|
||||
length?: 12 | 15 | 18 | 21 | 24,
|
||||
hdPath?: readonly Slip10RawIndex[],
|
||||
prefix?: string,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
static deserialize(serialization: string, password: string): Promise<Secp256k1Wallet>;
|
||||
static deserializeWithEncryptionKey(
|
||||
serialization: string,
|
||||
encryptionKey: Uint8Array,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
private static deserializeType1;
|
||||
/** Base secret */
|
||||
private readonly secret;
|
||||
/** Derivation instrations */
|
||||
private readonly accounts;
|
||||
/** Derived data */
|
||||
private readonly pubkey;
|
||||
private readonly privkey;
|
||||
private constructor();
|
||||
get mnemonic(): string;
|
||||
private get address();
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(address: string, message: Uint8Array, prehashType?: PrehashType): Promise<StdSignature>;
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* @param password The user provided password used to generate an encryption key via a KDF.
|
||||
* This is not normalized internally (see "Unicode normalization" to learn more).
|
||||
*/
|
||||
serialize(password: string): Promise<string>;
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* This is an advanced alternative of calling `serialize(password)` directly, which allows you to
|
||||
* offload the KDF execution to an non-UI thread (e.g. in a WebWorker).
|
||||
*
|
||||
* The caller is responsible for ensuring the key was derived with the given kdf options. If this
|
||||
* is not the case, the wallet cannot be restored with the original password.
|
||||
*/
|
||||
serializeWithEncryptionKey(encryptionKey: Uint8Array, kdfConfiguration: KdfConfiguration): Promise<string>;
|
||||
}
|
96
packages/sdk38/types/wallet.d.ts
vendored
96
packages/sdk38/types/wallet.d.ts
vendored
@ -17,45 +17,12 @@ export interface OfflineSigner {
|
||||
*/
|
||||
readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise<StdSignature>;
|
||||
}
|
||||
export declare function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array;
|
||||
/**
|
||||
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
|
||||
* with 0-based account index `a`.
|
||||
*/
|
||||
export declare function makeCosmoshubPath(a: number): readonly Slip10RawIndex[];
|
||||
/**
|
||||
* This interface describes a JSON object holding the encrypted wallet and the meta data.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletSerialization {
|
||||
/** A format+version identifier for this serialization format */
|
||||
readonly type: string;
|
||||
/** Information about the key derivation function (i.e. password to encrytion key) */
|
||||
readonly kdf: KdfConfiguration;
|
||||
/** Information about the symmetric encryption */
|
||||
readonly encryption: {
|
||||
/**
|
||||
* An algorithm identifier, such as "xchacha20poly1305-ietf".
|
||||
*/
|
||||
readonly algorithm: string;
|
||||
/** A map of algorithm-specific parameters */
|
||||
readonly params: Record<string, unknown>;
|
||||
};
|
||||
/** An instance of Secp256k1WalletData, which is stringified, encrypted and base64 encoded. */
|
||||
readonly data: string;
|
||||
}
|
||||
/**
|
||||
* The data of a wallet serialization that is encrypted.
|
||||
* All fields in here must be JSON types.
|
||||
*/
|
||||
export interface Secp256k1WalletData {
|
||||
readonly mnemonic: string;
|
||||
readonly accounts: ReadonlyArray<{
|
||||
readonly algo: string;
|
||||
readonly hdPath: string;
|
||||
readonly prefix: string;
|
||||
}>;
|
||||
}
|
||||
export declare function extractKdfConfiguration(serialization: string): KdfConfiguration;
|
||||
export interface KdfConfiguration {
|
||||
/**
|
||||
* An algorithm identifier, such as "argon2id" or "scrypt".
|
||||
@ -65,64 +32,3 @@ export interface KdfConfiguration {
|
||||
readonly params: Record<string, unknown>;
|
||||
}
|
||||
export declare function executeKdf(password: string, configuration: KdfConfiguration): Promise<Uint8Array>;
|
||||
export declare class Secp256k1Wallet implements OfflineSigner {
|
||||
/**
|
||||
* Restores a wallet from the given BIP39 mnemonic.
|
||||
*
|
||||
* @param mnemonic Any valid English mnemonic.
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
static fromMnemonic(
|
||||
mnemonic: string,
|
||||
hdPath?: readonly Slip10RawIndex[],
|
||||
prefix?: string,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
/**
|
||||
* Generates a new wallet with a BIP39 mnemonic of the given length.
|
||||
*
|
||||
* @param length The number of words in the mnemonic (12, 15, 18, 21 or 24).
|
||||
* @param hdPath The BIP-32/SLIP-10 derivation path. Defaults to the Cosmos Hub/ATOM path `m/44'/118'/0'/0/0`.
|
||||
* @param prefix The bech32 address prefix (human readable part). Defaults to "cosmos".
|
||||
*/
|
||||
static generate(
|
||||
length?: 12 | 15 | 18 | 21 | 24,
|
||||
hdPath?: readonly Slip10RawIndex[],
|
||||
prefix?: string,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
static deserialize(serialization: string, password: string): Promise<Secp256k1Wallet>;
|
||||
static deserializeWithEncryptionKey(
|
||||
serialization: string,
|
||||
encryptionKey: Uint8Array,
|
||||
): Promise<Secp256k1Wallet>;
|
||||
private static deserializeType1;
|
||||
/** Base secret */
|
||||
private readonly secret;
|
||||
/** Derivation instrations */
|
||||
private readonly accounts;
|
||||
/** Derived data */
|
||||
private readonly pubkey;
|
||||
private readonly privkey;
|
||||
private constructor();
|
||||
get mnemonic(): string;
|
||||
private get address();
|
||||
getAccounts(): Promise<readonly AccountData[]>;
|
||||
sign(address: string, message: Uint8Array, prehashType?: PrehashType): Promise<StdSignature>;
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* @param password The user provided password used to generate an encryption key via a KDF.
|
||||
* This is not normalized internally (see "Unicode normalization" to learn more).
|
||||
*/
|
||||
serialize(password: string): Promise<string>;
|
||||
/**
|
||||
* Generates an encrypted serialization of this wallet.
|
||||
*
|
||||
* This is an advanced alternative of calling `serialize(password)` directly, which allows you to
|
||||
* offload the KDF execution to an non-UI thread (e.g. in a WebWorker).
|
||||
*
|
||||
* The caller is responsible for ensuring the key was derived with the given kdf options. If this
|
||||
* is not the case, the wallet cannot be restored with the original password.
|
||||
*/
|
||||
serializeWithEncryptionKey(encryptionKey: Uint8Array, kdfConfiguration: KdfConfiguration): Promise<string>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user