Re-implement bip39 (#966)

* Re-implement bip39

* Use pbkdf2 from subtle

* Pull out and fix getSubtle()

* Remove unused pbkdf2 dependency

* Remove stream fallback (remove stream-browserify)

* Pull out pbkdf2Sha512

* Pull out pbkdf2Sha512Subtle

* Pull out pbkdf2Sha512Crypto

* Disable "Test Case 4" because it takes a long time

* Deactivate pbkdf2Sha512Subtle/pbkdf2Sha512Crypto tests when API unavailable

* Let getCryptoModule detect fallback modules

* Create noble implementation of pbkdf2Sha512

* Add CHANGELOG entry
This commit is contained in:
Simon Warta
2022-02-28 17:08:08 +01:00
committed by GitHub
30 changed files with 2786 additions and 596 deletions
Generated
+1 -147
View File
@@ -841,10 +841,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"bindings",
"npm:1.5.0"
],
[
"bip39",
"npm:3.0.4"
],
[
"bl",
"npm:4.1.0"
@@ -929,10 +925,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"chrome-trace-event",
"npm:1.0.3"
],
[
"cipher-base",
"npm:1.0.4"
],
[
"clean-stack",
"npm:2.2.0"
@@ -1037,14 +1029,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"cosmjs-types",
"npm:0.4.0"
],
[
"create-hash",
"npm:1.2.0"
],
[
"create-hmac",
"npm:1.1.7"
],
[
"cross-spawn",
"npm:7.0.3"
@@ -1913,10 +1897,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"marked",
"npm:4.0.10"
],
[
"md5.js",
"npm:1.3.5"
],
[
"media-typer",
"npm:0.3.0"
@@ -2181,10 +2161,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"path-type",
"npm:4.0.0"
],
[
"pbkdf2",
"npm:3.1.2"
],
[
"picomatch",
"npm:2.2.3"
@@ -2379,7 +2355,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
],
[
"safe-buffer",
"npm:5.2.1"
"npm:5.1.2"
],
[
"safer-buffer",
@@ -2409,10 +2385,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"setprototypeof",
"npm:1.1.1"
],
[
"sha.js",
"npm:2.4.11"
],
[
"shallow-clone",
"npm:3.0.1"
@@ -2521,10 +2493,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"statuses",
"npm:1.5.0"
],
[
"stream-browserify",
"npm:3.0.0"
],
[
"streamroller",
"npm:3.0.2"
@@ -3220,7 +3188,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["prettier", "npm:2.4.1"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
@@ -3328,7 +3295,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["readonly-date", "npm:1.0.0"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
@@ -3359,7 +3325,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/node", "npm:15.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:4.28.4"],
["@typescript-eslint/parser", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:4.28.4"],
["bip39", "npm:3.0.4"],
["bn.js", "npm:5.2.0"],
["buffer", "npm:6.0.3"],
["elliptic", "npm:6.5.4"],
@@ -3384,7 +3349,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["prettier", "npm:2.4.1"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
@@ -3706,7 +3670,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["protobufjs", "npm:6.10.2"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
@@ -3811,7 +3774,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["readonly-date", "npm:1.0.0"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
@@ -4716,13 +4678,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["@types/node", [
["npm:11.11.6", {
"packageLocation": "./.yarn/cache/@types-node-npm-11.11.6-40abad0842-075f1c011c.zip/node_modules/@types/node/",
"packageDependencies": [
["@types/node", "npm:11.11.6"]
],
"linkType": "HARD",
}],
["npm:13.13.52", {
"packageLocation": "./.yarn/cache/@types-node-npm-13.13.52-95159539bb-8f1afff497.zip/node_modules/@types/node/",
"packageDependencies": [
@@ -6287,19 +6242,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["bip39", [
["npm:3.0.4", {
"packageLocation": "./.yarn/cache/bip39-npm-3.0.4-7c69c9182f-79ce1600a0.zip/node_modules/bip39/",
"packageDependencies": [
["bip39", "npm:3.0.4"],
["@types/node", "npm:11.11.6"],
["create-hash", "npm:1.2.0"],
["pbkdf2", "npm:3.1.2"],
["randombytes", "npm:2.1.0"]
],
"linkType": "HARD",
}]
]],
["bl", [
["npm:4.1.0", {
"packageLocation": "./.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-9e8521fa7e.zip/node_modules/bl/",
@@ -6580,17 +6522,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["cipher-base", [
["npm:1.0.4", {
"packageLocation": "./.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-47d3568dbc.zip/node_modules/cipher-base/",
"packageDependencies": [
["cipher-base", "npm:1.0.4"],
["inherits", "npm:2.0.4"],
["safe-buffer", "npm:5.2.1"]
],
"linkType": "HARD",
}]
]],
["clean-stack", [
["npm:2.2.0", {
"packageLocation": "./.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-2ac8cd2b2f.zip/node_modules/clean-stack/",
@@ -6900,35 +6831,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["create-hash", [
["npm:1.2.0", {
"packageLocation": "./.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-02a6ae3bb9.zip/node_modules/create-hash/",
"packageDependencies": [
["create-hash", "npm:1.2.0"],
["cipher-base", "npm:1.0.4"],
["inherits", "npm:2.0.4"],
["md5.js", "npm:1.3.5"],
["ripemd160", "npm:2.0.2"],
["sha.js", "npm:2.4.11"]
],
"linkType": "HARD",
}]
]],
["create-hmac", [
["npm:1.1.7", {
"packageLocation": "./.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-ba12bb2257.zip/node_modules/create-hmac/",
"packageDependencies": [
["create-hmac", "npm:1.1.7"],
["cipher-base", "npm:1.0.4"],
["create-hash", "npm:1.2.0"],
["inherits", "npm:2.0.4"],
["ripemd160", "npm:2.0.2"],
["safe-buffer", "npm:5.2.1"],
["sha.js", "npm:2.4.11"]
],
"linkType": "HARD",
}]
]],
["cross-spawn", [
["npm:7.0.3", {
"packageLocation": "./.yarn/cache/cross-spawn-npm-7.0.3-e4ff3e65b3-671cc7c728.zip/node_modules/cross-spawn/",
@@ -9615,18 +9517,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["md5.js", [
["npm:1.3.5", {
"packageLocation": "./.yarn/cache/md5.js-npm-1.3.5-130901125a-098494d885.zip/node_modules/md5.js/",
"packageDependencies": [
["md5.js", "npm:1.3.5"],
["hash-base", "npm:3.1.0"],
["inherits", "npm:2.0.4"],
["safe-buffer", "npm:5.2.1"]
],
"linkType": "HARD",
}]
]],
["media-typer", [
["npm:0.3.0", {
"packageLocation": "./.yarn/cache/media-typer-npm-0.3.0-8674f8f0f5-af1b38516c.zip/node_modules/media-typer/",
@@ -10410,20 +10300,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["pbkdf2", [
["npm:3.1.2", {
"packageLocation": "./.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-2c950a100b.zip/node_modules/pbkdf2/",
"packageDependencies": [
["pbkdf2", "npm:3.1.2"],
["create-hash", "npm:1.2.0"],
["create-hmac", "npm:1.1.7"],
["ripemd160", "npm:2.0.2"],
["safe-buffer", "npm:5.2.1"],
["sha.js", "npm:2.4.11"]
],
"linkType": "HARD",
}]
]],
["picomatch", [
["npm:2.2.3", {
"packageLocation": "./.yarn/cache/picomatch-npm-2.2.3-3797e21cf0-45e2b882b5.zip/node_modules/picomatch/",
@@ -11134,17 +11010,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["sha.js", [
["npm:2.4.11", {
"packageLocation": "./.yarn/cache/sha.js-npm-2.4.11-14868df4ca-ebd3f59d4b.zip/node_modules/sha.js/",
"packageDependencies": [
["sha.js", "npm:2.4.11"],
["inherits", "npm:2.0.4"],
["safe-buffer", "npm:5.2.1"]
],
"linkType": "HARD",
}]
]],
["shallow-clone", [
["npm:3.0.1", {
"packageLocation": "./.yarn/cache/shallow-clone-npm-3.0.1-dab5873d0d-39b3dd9630.zip/node_modules/shallow-clone/",
@@ -11443,17 +11308,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
["stream-browserify", [
["npm:3.0.0", {
"packageLocation": "./.yarn/cache/stream-browserify-npm-3.0.0-4c0bd97245-4c47ef64d6.zip/node_modules/stream-browserify/",
"packageDependencies": [
["stream-browserify", "npm:3.0.0"],
["inherits", "npm:2.0.4"],
["readable-stream", "npm:3.6.0"]
],
"linkType": "HARD",
}]
]],
["streamroller", [
["npm:3.0.2", {
"packageLocation": "./.yarn/cache/streamroller-npm-3.0.2-6d7ba8035a-1f323824f0.zip/node_modules/streamroller/",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -39,9 +39,13 @@ and this project adheres to
- @cosmjs/ledger-amino: Renamed `LaunchpadLedger` to `LedgerConnector` ([#955])
- @cosmjs/encoding: Created `toBech32()` and `fromBech32()`. Class Bech32 is now
deprecated and should not longer be used. ([#1053])
- @cosmjs/crypto: Use a custom BIP-39 implementation to reduce external
dependencies. This should also reduce the bundle size as only the English
wordlist is shipped. ([#966])
[#927]: https://github.com/cosmos/cosmjs/issues/927
[#955]: https://github.com/cosmos/cosmjs/issues/955
[#966]: https://github.com/cosmos/cosmjs/pull/966
[#989]: https://github.com/cosmos/cosmjs/issues/989
[#994]: https://github.com/cosmos/cosmjs/issues/994
[#1011]: https://github.com/cosmos/cosmjs/issues/1011
+3
View File
@@ -23,6 +23,9 @@ The code in packages/json-rpc was forked from https://github.com/iov-one/iov-cor
The code in packages/socket and scripts/socketserver was forked from the folders packages/iov-socket and scripts/socketserver respectively of https://github.com/iov-one/iov-core at tag v2.5.0 on 2020-06-24.
The code in packages/crypto/src/bip39.ts contains code forked from bitcoinjs/bip39 (https://github.com/bitcoinjs/bip39/blob/v3.0.4/LICENSE)
on 2021-12-16. Copyright (c) 2014, Wei Lu and Daniel Cousens.
Copyright 2018-2020 IOV SAS
Copyright 2020-2021 Confio OÜ
Copyright 2020 Simon Warta
-1
View File
@@ -74,7 +74,6 @@
"prettier": "^2.4.1",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
+1 -1
View File
@@ -26,7 +26,7 @@ module.exports = [
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
stream: false,
string_decoder: false,
},
},
-1
View File
@@ -84,7 +84,6 @@
"readonly-date": "^1.0.0",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
@@ -29,7 +29,7 @@ module.exports = [
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
stream: false,
string_decoder: false,
},
},
-2
View File
@@ -45,7 +45,6 @@
"@cosmjs/math": "workspace:packages/math",
"@cosmjs/utils": "workspace:packages/utils",
"@noble/hashes": "^1",
"bip39": "^3.0.2",
"bn.js": "^5.2.0",
"elliptic": "^6.5.3",
"libsodium-wrappers": "^0.7.6"
@@ -84,7 +83,6 @@
"prettier": "^2.4.1",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
+281 -3
View File
@@ -1,8 +1,49 @@
import { fromHex } from "@cosmjs/encoding";
import { fromAscii, fromBase64, fromHex } from "@cosmjs/encoding";
import { Bip39 } from "./bip39";
import { EnglishMnemonic } from "./englishmnemonic";
import { Bip39, EnglishMnemonic, entropyToMnemonic, mnemonicToEntropy } from "./bip39";
import { sha256 } from "./sha";
import bip39Vectors from "./testdata/bip39.json";
import wordlists from "./testdata/bip39_wordlists.json";
describe("entropyToMnemonic", () => {
it("works", () => {
// From https://iancoleman.io/bip39/
expect(entropyToMnemonic(fromHex("a323224e6b13d31942509dc4e2e579be3d5bb7f2"))).toEqual(
"permit boil near stomach diamond million announce beauty shaft blame fury ladder stick swim slab",
);
});
it("works for all the test vectors", () => {
// Test vectors from https://github.com/trezor/python-mnemonic/blob/b502451a33a440783926e04428115e0bed87d01f/vectors.json
// plus similar vectors generated for the missing lengths 15 and 21 words
const { "12": vec12, "15": vec15, "18": vec18, "21": vec21, "24": vec24 } = bip39Vectors.encoding;
for (const vectors of [vec12, vec15, vec18, vec21, vec24]) {
for (const { entropy, mnemonic } of vectors) {
expect(entropyToMnemonic(fromHex(entropy)).toString()).toEqual(mnemonic);
}
}
});
});
describe("mnemonicToEntropy", () => {
it("works", () => {
// From https://iancoleman.io/bip39/
expect(
mnemonicToEntropy(
"permit boil near stomach diamond million announce beauty shaft blame fury ladder stick swim slab",
),
).toEqual(fromHex("a323224e6b13d31942509dc4e2e579be3d5bb7f2"));
});
it("works for all the test vectors", () => {
const { "12": vec12, "15": vec15, "18": vec18, "21": vec21, "24": vec24 } = bip39Vectors.encoding;
for (const vectors of [vec12, vec15, vec18, vec21, vec24]) {
for (const { entropy, mnemonic } of vectors) {
expect(mnemonicToEntropy(mnemonic)).toEqual(fromHex(entropy));
}
}
});
});
describe("Bip39", () => {
describe("encode", () => {
@@ -414,3 +455,240 @@ describe("Bip39", () => {
});
});
});
describe("EnglishMnemonic", () => {
describe("wordlist", () => {
it("matches the words from the bitcoin/bips/bip-0039 spec", () => {
const lineFeed = 0x0a;
const bip39EnglishTxt = fromBase64(wordlists.english);
// Ensure we loaded the correct english.txt from https://github.com/bitcoin/bips/tree/master/bip-0039
const checksum = sha256(bip39EnglishTxt);
expect(checksum).toEqual(fromHex("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"));
const wordsFromSpec: string[] = [];
let start = 0; // the start cursor marks the first byte of the word
let end = 0; // the end cursor marks the line feed byte
while (end < bip39EnglishTxt.length - 1) {
end = start;
while (bip39EnglishTxt[end] !== lineFeed) end++;
const slice = bip39EnglishTxt.slice(start, end);
wordsFromSpec.push(fromAscii(slice));
start = end + 1;
}
expect(EnglishMnemonic.wordlist).toEqual(wordsFromSpec);
});
});
it("works for valid inputs", () => {
expect(() => {
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
);
}).not.toThrow();
});
it("rejects invalid whitespacing", () => {
// extra space (leading, middle, trailing)
expect(
() =>
new EnglishMnemonic(
" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
),
).toThrowError(/invalid mnemonic format/i);
// newline, tab
expect(
() =>
new EnglishMnemonic(
"abandon\nabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon\tabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
});
it("rejects disallowed letters", () => {
// Disallowed letters in words (capital, number, special char)
expect(
() =>
new EnglishMnemonic(
"Abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon Abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"route66 abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon route66 abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"lötkolben abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon lötkolben abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
});
it("word counts other than 12, 15, 18, 21, 24", () => {
// too few and too many words (11, 13, 17, 19, 23, 25)
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid word count(.*)got: 11/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid word count(.*)got: 13/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
),
).toThrowError(/invalid word count(.*)got: 17/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
),
).toThrowError(/invalid word count(.*)got: 19/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
),
).toThrowError(/invalid word count(.*)got: 23/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
),
).toThrowError(/invalid word count(.*)got: 25/i);
});
it("rejects invalid checksums", () => {
// 12x, 15x, 18x, 21x, 24x "zoo"
expect(() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo")).toThrowError(
/invalid mnemonic checksum/i,
);
expect(
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() =>
new EnglishMnemonic(
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() =>
new EnglishMnemonic(
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
),
).toThrowError(/invalid mnemonic checksum/i);
});
it("rejects valid mnemonics of other languages", () => {
// valid Spanish and Italian bip39 mnemonics
expect(
() =>
new EnglishMnemonic(
"humo odio oriente colina taco fingir salto geranio glaciar academia suave vigor",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"yema folleto tos llave obtener natural fruta deseo laico sopa novato lazo imponer afinar vena hoja zarza cama",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"burla plaza arroz ronda pregunta vacuna veloz boina retiro exento prensa tortuga cabeza pilar anual molino molde fiesta masivo jefe leve fatiga clase plomo",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"braccio trincea armonia emiro svedese lepre stridulo metallo baldo rasente potassio rilassato",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"riparato arrosto globulo singolo bozzolo roba pirolisi ultimato padrone munto leggero avanzato monetario guanto lorenzo latino inoltrare modulo",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"promessa mercurio spessore snodo trave risata mecenate vichingo ceto orecchino vissuto risultato canino scarso futile fune epilogo uovo inedito apatico folata egoismo rifugio coma",
),
).toThrowError(/contains invalid word/i);
});
describe("toString", () => {
it("works", () => {
const original =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const mnemonic = new EnglishMnemonic(original);
expect(mnemonic.toString()).toEqual(original);
});
});
});
File diff suppressed because it is too large Load Diff
-242
View File
@@ -1,242 +0,0 @@
import { fromAscii, fromBase64, fromHex } from "@cosmjs/encoding";
import { EnglishMnemonic } from "./englishmnemonic";
import { sha256 } from "./sha";
import wordlists from "./testdata/bip39_wordlists.json";
describe("EnglishMnemonic", () => {
describe("wordlist", () => {
it("matches the words from the bitcoin/bips/bip-0039 spec", () => {
const lineFeed = 0x0a;
const bip39EnglishTxt = fromBase64(wordlists.english);
// Ensure we loaded the correct english.txt from https://github.com/bitcoin/bips/tree/master/bip-0039
const checksum = sha256(bip39EnglishTxt);
expect(checksum).toEqual(fromHex("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"));
const wordsFromSpec: string[] = [];
let start = 0; // the start cursor marks the first byte of the word
let end = 0; // the end cursor marks the line feed byte
while (end < bip39EnglishTxt.length - 1) {
end = start;
while (bip39EnglishTxt[end] !== lineFeed) end++;
const slice = bip39EnglishTxt.slice(start, end);
wordsFromSpec.push(fromAscii(slice));
start = end + 1;
}
expect(EnglishMnemonic.wordlist).toEqual(wordsFromSpec);
});
});
it("works for valid inputs", () => {
expect(() => {
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon admit",
);
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
);
}).not.toThrow();
});
it("rejects invalid whitespacing", () => {
// extra space (leading, middle, trailing)
expect(
() =>
new EnglishMnemonic(
" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
),
).toThrowError(/invalid mnemonic format/i);
// newline, tab
expect(
() =>
new EnglishMnemonic(
"abandon\nabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon\tabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
});
it("rejects disallowed letters", () => {
// Disallowed letters in words (capital, number, special char)
expect(
() =>
new EnglishMnemonic(
"Abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon Abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"route66 abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon route66 abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"lötkolben abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon lötkolben abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid mnemonic format/i);
});
it("word counts other than 12, 15, 18, 21, 24", () => {
// too few and too many words (11, 13, 17, 19, 23, 25)
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid word count(.*)got: 11/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
),
).toThrowError(/invalid word count(.*)got: 13/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
),
).toThrowError(/invalid word count(.*)got: 17/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
),
).toThrowError(/invalid word count(.*)got: 19/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
),
).toThrowError(/invalid word count(.*)got: 23/i);
expect(
() =>
new EnglishMnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
),
).toThrowError(/invalid word count(.*)got: 25/i);
});
it("rejects invalid checksums", () => {
// 12x, 15x, 18x, 21x, 24x "zoo"
expect(() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo")).toThrowError(
/invalid mnemonic checksum/i,
);
expect(
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() => new EnglishMnemonic("zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() =>
new EnglishMnemonic(
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
),
).toThrowError(/invalid mnemonic checksum/i);
expect(
() =>
new EnglishMnemonic(
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",
),
).toThrowError(/invalid mnemonic checksum/i);
});
it("rejects valid mnemonics of other languages", () => {
// valid Spanish and Italian bip39 mnemonics
expect(
() =>
new EnglishMnemonic(
"humo odio oriente colina taco fingir salto geranio glaciar academia suave vigor",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"yema folleto tos llave obtener natural fruta deseo laico sopa novato lazo imponer afinar vena hoja zarza cama",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"burla plaza arroz ronda pregunta vacuna veloz boina retiro exento prensa tortuga cabeza pilar anual molino molde fiesta masivo jefe leve fatiga clase plomo",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"braccio trincea armonia emiro svedese lepre stridulo metallo baldo rasente potassio rilassato",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"riparato arrosto globulo singolo bozzolo roba pirolisi ultimato padrone munto leggero avanzato monetario guanto lorenzo latino inoltrare modulo",
),
).toThrowError(/contains invalid word/i);
expect(
() =>
new EnglishMnemonic(
"promessa mercurio spessore snodo trave risata mecenate vichingo ceto orecchino vissuto risultato canino scarso futile fune epilogo uovo inedito apatico folata egoismo rifugio coma",
),
).toThrowError(/contains invalid word/i);
});
describe("toString", () => {
it("works", () => {
const original =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const mnemonic = new EnglishMnemonic(original);
expect(mnemonic.toString()).toEqual(original);
});
});
});
-39
View File
@@ -1,39 +0,0 @@
import * as bip39 from "bip39";
export class EnglishMnemonic {
public static readonly wordlist: readonly string[] = bip39.wordlists.english;
// list of space separated lower case words (1 or more)
private static readonly mnemonicMatcher = /^[a-z]+( [a-z]+)*$/;
private readonly data: string;
public constructor(mnemonic: string) {
if (!EnglishMnemonic.mnemonicMatcher.test(mnemonic)) {
throw new Error("Invalid mnemonic format");
}
const words = mnemonic.split(" ");
const allowedWordsLengths: readonly number[] = [12, 15, 18, 21, 24];
if (allowedWordsLengths.indexOf(words.length) === -1) {
throw new Error(
`Invalid word count in mnemonic (allowed: ${allowedWordsLengths} got: ${words.length})`,
);
}
for (const word of words) {
if (EnglishMnemonic.wordlist.indexOf(word) === -1) {
throw new Error("Mnemonic contains invalid word");
}
}
// Throws with informative error message if mnemonic is not valid
bip39.mnemonicToEntropy(mnemonic);
this.data = mnemonic;
}
public toString(): string {
return this.data;
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
export { Bip39 } from "./bip39";
export { EnglishMnemonic } from "./englishmnemonic";
export { Bip39, EnglishMnemonic } from "./bip39";
export { HashFunction } from "./hash";
export { Hmac } from "./hmac";
export { Keccak256, keccak256 } from "./keccak";
+183
View File
@@ -0,0 +1,183 @@
import { fromHex, toAscii, toUtf8 } from "@cosmjs/encoding";
import {
getCryptoModule,
getSubtle,
pbkdf2Sha512,
pbkdf2Sha512Crypto,
pbkdf2Sha512Noble,
pbkdf2Sha512Subtle,
} from "./pbkdf2";
interface TestVector {
secret: Uint8Array;
salt: Uint8Array;
iterations: number;
keylen: number;
expected: Uint8Array;
}
describe("pbkdf2", () => {
// https://github.com/randombit/botan/blob/master/src/tests/data/pbkdf/pbkdf2.vec#L70-L74
const botanTest: TestVector = {
secret: toAscii("xyz"),
salt: fromHex("0001020304050607"),
iterations: 10000,
keylen: 48,
expected: fromHex(
"DAF8A734327745EB63D19054DBD4018A682CEF11086A1BFB63FDBC16158C2F8B0742802F36AEF1B1DF92ACCBEA5D31A5",
),
};
// https://github.com/brycx/Test-Vector-Generation/blob/f88d152db/PBKDF2/pbkdf2-hmac-sha2-test-vectors.md
const brycxTests: TestVector[] = [
// Test Case 1
{
secret: toUtf8("password"),
salt: toUtf8("salt"),
iterations: 1,
keylen: 20,
expected: fromHex("867f70cf1ade02cff3752599a3a53dc4af34c7a6"),
},
// Test Case 2
{
secret: toUtf8("password"),
salt: toUtf8("salt"),
iterations: 2,
keylen: 20,
expected: fromHex("e1d9c16aa681708a45f5c7c4e215ceb66e011a2e"),
},
// Test Case 3
{
secret: toUtf8("password"),
salt: toUtf8("salt"),
iterations: 4096,
keylen: 20,
expected: fromHex("d197b1b33db0143e018b12f3d1d1479e6cdebdcc"),
},
// Test Case 4 (disabled by default because long running)
// {
// secret: toUtf8("password"),
// salt: toUtf8("salt"),
// iterations: 16777216,
// keylen: 20,
// expected: fromHex("6180a3ceabab45cc3964112c811e0131bca93a35"),
// },
// Test Case 5
{
secret: toUtf8("passwordPASSWORDpassword"),
salt: toUtf8("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
iterations: 4096,
keylen: 25,
expected: fromHex("8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868"),
},
// Test Case 6
{
secret: toUtf8("pass\0word"),
salt: toUtf8("sa\0lt"),
iterations: 4096,
keylen: 16,
expected: fromHex("9d9e9c4cd21fe4be24d5b8244c759665"),
},
// Test Case 7
{
secret: toUtf8("passwd"),
salt: toUtf8("salt"),
iterations: 1,
keylen: 128,
expected: fromHex(
"c74319d99499fc3e9013acff597c23c5baf0a0bec5634c46b8352b793e324723d55caa76b2b25c43402dcfdc06cdcf66f95b7d0429420b39520006749c51a04ef3eb99e576617395a178ba33214793e48045132928a9e9bf2661769fdc668f31798597aaf6da70dd996a81019726084d70f152baed8aafe2227c07636c6ddece",
),
},
// Test Case 8
{
secret: toUtf8("Password"),
salt: toUtf8("NaCl"),
iterations: 80000,
keylen: 128,
expected: fromHex(
"e6337d6fbeb645c794d4a9b5b75b7b30dac9ac50376a91df1f4460f6060d5addb2c1fd1f84409abacc67de7eb4056e6bb06c2d82c3ef4ccd1bded0f675ed97c65c33d39f81248454327aa6d03fd049fc5cbb2b5e6dac08e8ace996cdc960b1bd4530b7e754773d75f67a733fdb99baf6470e42ffcb753c15c352d4800fb6f9d6",
),
},
// Test Case 9
{
secret: toUtf8("Password"),
salt: toUtf8("sa\0lt"),
iterations: 4096,
keylen: 256,
expected: fromHex(
"10176fb32cb98cd7bb31e2bb5c8f6e425c103333a2e496058e3fd2bd88f657485c89ef92daa0668316bc23ebd1ef88f6dd14157b2320b5d54b5f26377c5dc279b1dcdec044bd6f91b166917c80e1e99ef861b1d2c7bce1b961178125fb86867f6db489a2eae0022e7bc9cf421f044319fac765d70cb89b45c214590e2ffb2c2b565ab3b9d07571fde0027b1dc57f8fd25afa842c1056dd459af4074d7510a0c020b914a5e202445d4d3f151070589dd6a2554fc506018c4f001df6239643dc86771286ae4910769d8385531bba57544d63c3640b90c98f1445ebdd129475e02086b600f0beb5b05cc6ca9b3633b452b7dad634e9336f56ec4c3ac0b4fe54ced8",
),
},
];
describe("pbkdf2Sha512", () => {
it("works", async () => {
{
const { secret, salt, iterations, keylen, expected } = botanTest;
const hash = await pbkdf2Sha512(secret, salt, iterations, keylen);
expect(hash).toEqual(expected);
}
for (const [index, test] of brycxTests.entries()) {
const { secret, salt, iterations, keylen, expected } = test;
const hash = await pbkdf2Sha512(secret, salt, iterations, keylen);
expect(hash).withContext(`brycx tests index ${index}`).toEqual(expected);
}
});
});
describe("pbkdf2Sha512Subtle", () => {
it("works", async () => {
const subtle = await getSubtle();
if (!subtle) pending("Subtle is not available in this environment");
{
const { secret, salt, iterations, keylen, expected } = botanTest;
const hash = await pbkdf2Sha512Subtle(subtle, secret, salt, iterations, keylen);
expect(hash).toEqual(expected);
}
for (const [index, test] of brycxTests.entries()) {
const { secret, salt, iterations, keylen, expected } = test;
const hash = await pbkdf2Sha512Subtle(subtle, secret, salt, iterations, keylen);
expect(hash).withContext(`brycx tests index ${index}`).toEqual(expected);
}
});
});
describe("pbkdf2Sha512Crypto", () => {
it("works", async () => {
const crypto = await getCryptoModule();
if (!crypto) pending("The crypto module is not available in this environment");
{
const { secret, salt, iterations, keylen, expected } = botanTest;
const hash = await pbkdf2Sha512Crypto(crypto, secret, salt, iterations, keylen);
expect(hash).toEqual(expected);
}
for (const [index, test] of brycxTests.entries()) {
const { secret, salt, iterations, keylen, expected } = test;
const hash = await pbkdf2Sha512Crypto(crypto, secret, salt, iterations, keylen);
expect(hash).withContext(`brycx tests index ${index}`).toEqual(expected);
}
});
});
describe("pbkdf2Sha512Noble", () => {
it("works", async () => {
{
const { secret, salt, iterations, keylen, expected } = botanTest;
const hash = await pbkdf2Sha512Noble(secret, salt, iterations, keylen);
expect(hash).toEqual(expected);
}
for (const [index, test] of brycxTests.entries()) {
const { secret, salt, iterations, keylen, expected } = test;
const hash = await pbkdf2Sha512Noble(secret, salt, iterations, keylen);
expect(hash).withContext(`brycx tests index ${index}`).toEqual(expected);
}
}, 120_000);
});
});
+119
View File
@@ -0,0 +1,119 @@
import { assert } from "@cosmjs/utils";
import { pbkdf2Async as noblePbkdf2Async } from "@noble/hashes/pbkdf2";
import { sha512 as nobleSha512 } from "@noble/hashes/sha512";
/**
* Returns the Node.js crypto module when available and `undefined`
* otherwise.
*
* Detects an unimplemented fallback module from Webpack 5 and returns
* `undefined` in that case.
*/
export async function getCryptoModule(): Promise<any | undefined> {
try {
const crypto = await import("crypto");
// We get `Object{default: Object{}}` as a fallback when using
// `crypto: false` in Webpack 5, which we interprete as unavailable.
if (typeof crypto === "object" && Object.keys(crypto).length <= 1) {
return undefined;
}
return crypto;
} catch {
return undefined;
}
}
export async function getSubtle(): Promise<any | undefined> {
const g: any = globalThis;
let subtle = g.crypto && g.crypto.subtle;
if (!subtle) {
const crypto = await getCryptoModule();
if (crypto && crypto.webcrypto && crypto.webcrypto.subtle) {
subtle = crypto.webcrypto.subtle;
}
}
return subtle;
}
export async function pbkdf2Sha512Subtle(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
subtle: any,
secret: Uint8Array,
salt: Uint8Array,
iterations: number,
keylen: number,
): Promise<Uint8Array> {
assert(subtle, "Argument subtle is falsy");
assert(typeof subtle === "object", "Argument subtle is not of type object");
assert(typeof subtle.importKey === "function", "subtle.importKey is not a function");
assert(typeof subtle.deriveBits === "function", "subtle.deriveBits is not a function");
return subtle.importKey("raw", secret, { name: "PBKDF2" }, false, ["deriveBits"]).then((key: Uint8Array) =>
subtle
.deriveBits(
{
name: "PBKDF2",
salt: salt,
iterations: iterations,
hash: { name: "SHA-512" },
},
key,
keylen * 8,
)
.then((buffer: ArrayBuffer) => new Uint8Array(buffer)),
);
}
export async function pbkdf2Sha512Crypto(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
crypto: any,
secret: Uint8Array,
salt: Uint8Array,
iterations: number,
keylen: number,
): Promise<Uint8Array> {
assert(crypto, "Argument crypto is falsy");
assert(typeof crypto === "object", "Argument crypto is not of type object");
assert(typeof crypto.pbkdf2 === "function", "crypto.pbkdf2 is not a function");
return new Promise((resolve, reject) => {
crypto.pbkdf2(secret, salt, iterations, keylen, "sha512", (error: any, result: any) => {
if (error) {
reject(error);
} else {
resolve(Uint8Array.from(result));
}
});
});
}
export async function pbkdf2Sha512Noble(
secret: Uint8Array,
salt: Uint8Array,
iterations: number,
keylen: number,
): Promise<Uint8Array> {
return noblePbkdf2Async(nobleSha512, secret, salt, { c: iterations, dkLen: keylen });
}
/**
* A pbkdf2 implementation for BIP39. This is not exported at package level and thus a private API.
*/
export async function pbkdf2Sha512(
secret: Uint8Array,
salt: Uint8Array,
iterations: number,
keylen: number,
): Promise<Uint8Array> {
const subtle = await getSubtle();
if (subtle) {
return pbkdf2Sha512Subtle(subtle, secret, salt, iterations, keylen);
} else {
const crypto = await getCryptoModule();
if (crypto) {
return pbkdf2Sha512Crypto(crypto, secret, salt, iterations, keylen);
} else {
return pbkdf2Sha512Noble(secret, salt, iterations, keylen);
}
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ module.exports = [
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
stream: false,
string_decoder: false,
},
},
-1
View File
@@ -79,7 +79,6 @@
"prettier": "^2.4.1",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
+1 -1
View File
@@ -26,7 +26,7 @@ module.exports = [
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
stream: false,
string_decoder: false,
},
},
-1
View File
@@ -84,7 +84,6 @@
"readonly-date": "^1.0.0",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
+1 -1
View File
@@ -32,7 +32,7 @@ module.exports = [
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
stream: false,
string_decoder: false,
},
},
+5 -113
View File
@@ -329,7 +329,6 @@ __metadata:
prettier: ^2.4.1
ses: ^0.11.0
source-map-support: ^0.5.19
stream-browserify: ^3.0.0
ts-node: ^8
typedoc: ^0.22
typescript: ~4.4
@@ -435,7 +434,6 @@ __metadata:
readonly-date: ^1.0.0
ses: ^0.11.0
source-map-support: ^0.5.19
stream-browserify: ^3.0.0
ts-node: ^8
typedoc: ^0.22
typescript: ~4.4
@@ -464,7 +462,6 @@ __metadata:
"@types/node": ^15.0.1
"@typescript-eslint/eslint-plugin": ^4.28
"@typescript-eslint/parser": ^4.28
bip39: ^3.0.2
bn.js: ^5.2.0
buffer: ^6.0.3
elliptic: ^6.5.3
@@ -489,7 +486,6 @@ __metadata:
prettier: ^2.4.1
ses: ^0.11.0
source-map-support: ^0.5.19
stream-browserify: ^3.0.0
ts-node: ^8
typedoc: ^0.22
typescript: ~4.4
@@ -799,7 +795,6 @@ __metadata:
protobufjs: ~6.10.2
ses: ^0.11.0
source-map-support: ^0.5.19
stream-browserify: ^3.0.0
ts-node: ^8
typedoc: ^0.22
typescript: ~4.4
@@ -900,7 +895,6 @@ __metadata:
readonly-date: ^1.0.0
ses: ^0.11.0
source-map-support: ^0.5.19
stream-browserify: ^3.0.0
ts-node: ^8
typedoc: ^0.22
typescript: ~4.4
@@ -1692,13 +1686,6 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:11.11.6":
version: 11.11.6
resolution: "@types/node@npm:11.11.6"
checksum: 075f1c011cf568e49701419acbcb55c24906b3bb5a34d9412a3b88f228a7a78401a5ad4d3e1cd6855c99aaea5ef96e37fc86ca097e50f06da92cf822befc1fff
languageName: node
linkType: hard
"@types/node@npm:>=13.7.0":
version: 15.9.0
resolution: "@types/node@npm:15.9.0"
@@ -2401,18 +2388,6 @@ __metadata:
languageName: node
linkType: hard
"bip39@npm:^3.0.2":
version: 3.0.4
resolution: "bip39@npm:3.0.4"
dependencies:
"@types/node": 11.11.6
create-hash: ^1.1.0
pbkdf2: ^3.0.9
randombytes: ^2.0.1
checksum: 79ce1600a03d1ba5053bdd4e6323f9463ec340764c7e52918b6c6b9dca81221940f2d9a65656447f108f9bc2c8d9ae8df319cca83bbd1dad63f53ef2768d9bae
languageName: node
linkType: hard
"bl@npm:^4.0.3":
version: 4.1.0
resolution: "bl@npm:4.1.0"
@@ -2670,16 +2645,6 @@ __metadata:
languageName: node
linkType: hard
"cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3":
version: 1.0.4
resolution: "cipher-base@npm:1.0.4"
dependencies:
inherits: ^2.0.1
safe-buffer: ^5.0.1
checksum: 47d3568dbc17431a339bad1fe7dff83ac0891be8206911ace3d3b818fc695f376df809bea406e759cdea07fff4b454fa25f1013e648851bec790c1d75763032e
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
@@ -2948,33 +2913,6 @@ __metadata:
languageName: node
linkType: hard
"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2":
version: 1.2.0
resolution: "create-hash@npm:1.2.0"
dependencies:
cipher-base: ^1.0.1
inherits: ^2.0.1
md5.js: ^1.3.4
ripemd160: ^2.0.1
sha.js: ^2.4.0
checksum: 02a6ae3bb9cd4afee3fabd846c1d8426a0e6b495560a977ba46120c473cb283be6aa1cace76b5f927cf4e499c6146fb798253e48e83d522feba807d6b722eaa9
languageName: node
linkType: hard
"create-hmac@npm:^1.1.4":
version: 1.1.7
resolution: "create-hmac@npm:1.1.7"
dependencies:
cipher-base: ^1.0.3
create-hash: ^1.1.0
inherits: ^2.0.1
ripemd160: ^2.0.0
safe-buffer: ^5.0.1
sha.js: ^2.4.8
checksum: ba12bb2257b585a0396108c72830e85f882ab659c3320c83584b1037f8ab72415095167ced80dc4ce8e446a8ecc4b2acf36d87befe0707d73b26cf9dc77440ed
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@@ -4394,7 +4332,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
@@ -5227,17 +5165,6 @@ __metadata:
languageName: node
linkType: hard
"md5.js@npm:^1.3.4":
version: 1.3.5
resolution: "md5.js@npm:1.3.5"
dependencies:
hash-base: ^3.0.0
inherits: ^2.0.1
safe-buffer: ^5.1.2
checksum: 098494d885684bcc4f92294b18ba61b7bd353c23147fbc4688c75b45cb8590f5a95fd4584d742415dcc52487f7a1ef6ea611cfa1543b0dc4492fe026357f3f0c
languageName: node
linkType: hard
"media-typer@npm:0.3.0":
version: 0.3.0
resolution: "media-typer@npm:0.3.0"
@@ -5947,19 +5874,6 @@ __metadata:
languageName: node
linkType: hard
"pbkdf2@npm:^3.0.9":
version: 3.1.2
resolution: "pbkdf2@npm:3.1.2"
dependencies:
create-hash: ^1.1.2
create-hmac: ^1.1.4
ripemd160: ^2.0.1
safe-buffer: ^5.0.1
sha.js: ^2.4.8
checksum: 2c950a100b1da72123449208e231afc188d980177d021d7121e96a2de7f2abbc96ead2b87d03d8fe5c318face097f203270d7e27908af9f471c165a4e8e69c92
languageName: node
linkType: hard
"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3":
version: 2.2.3
resolution: "picomatch@npm:2.2.3"
@@ -6210,7 +6124,7 @@ __metadata:
languageName: node
linkType: hard
"randombytes@npm:^2.0.1, randombytes@npm:^2.1.0":
"randombytes@npm:^2.1.0":
version: 2.1.0
resolution: "randombytes@npm:2.1.0"
dependencies:
@@ -6300,7 +6214,7 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0":
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0":
version: 3.6.0
resolution: "readable-stream@npm:3.6.0"
dependencies:
@@ -6481,7 +6395,7 @@ __metadata:
languageName: node
linkType: hard
"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1, ripemd160@npm:^2.0.2":
"ripemd160@npm:^2.0.2":
version: 2.0.2
resolution: "ripemd160@npm:2.0.2"
dependencies:
@@ -6516,7 +6430,7 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0":
"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491
@@ -6611,18 +6525,6 @@ __metadata:
languageName: node
linkType: hard
"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8":
version: 2.4.11
resolution: "sha.js@npm:2.4.11"
dependencies:
inherits: ^2.0.1
safe-buffer: ^5.0.1
bin:
sha.js: ./bin.js
checksum: ebd3f59d4b799000699097dadb831c8e3da3eb579144fd7eb7a19484cbcbb7aca3c68ba2bb362242eb09e33217de3b4ea56e4678184c334323eca24a58e3ad07
languageName: node
linkType: hard
"shallow-clone@npm:^3.0.0":
version: 3.0.1
resolution: "shallow-clone@npm:3.0.1"
@@ -6882,16 +6784,6 @@ __metadata:
languageName: node
linkType: hard
"stream-browserify@npm:^3.0.0":
version: 3.0.0
resolution: "stream-browserify@npm:3.0.0"
dependencies:
inherits: ~2.0.4
readable-stream: ^3.5.0
checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921
languageName: node
linkType: hard
"streamroller@npm:^3.0.2":
version: 3.0.2
resolution: "streamroller@npm:3.0.2"