Merge pull request #1355 from cosmos/revert-node-crypto

Revert "Remove pbkdf2Sha512Crypto and getCryptoModule"
This commit is contained in:
Simon Warta 2022-12-22 16:36:11 +01:00 committed by GitHub
commit 22ef61fa8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 7 deletions

View File

@ -12,10 +12,6 @@ and this project adheres to
- all: Add full support for Node.js 18 and run all CI tests with it ([#1240]).
- @cosmjs/tendermint-rpc: Remove unused `index` field from `RpcTxEvent` and
`TxEvent`. This is unset starting with Tendermint 0.34.
- @cosmjs/crypto: The pbkdf2 implementation for old Node.js versions
`pbkdf2Sha512Crypto` was removed. Node.js has sufficient support for WebCrypto
these days and we still have a pure-JS fallback implementation. This avoids
unnecessary problems around importing Node.js modules. ([#1341])
- @cosmjs/proto-signing: Make input and output of `decodePubkey` non-optional
([#1289]).
- @cosmjs/stargate: Remove unnecessary address prefix argument from
@ -31,7 +27,6 @@ and this project adheres to
[#1289]: https://github.com/cosmos/cosmjs/issues/1289
[#1291]: https://github.com/cosmos/cosmjs/issues/1291
[#1329]: https://github.com/cosmos/cosmjs/pull/1329
[#1341]: https://github.com/cosmos/cosmjs/issues/1341
## [0.29.5] - 2022-12-07

View File

@ -1,6 +1,13 @@
import { fromHex, toAscii, toUtf8 } from "@cosmjs/encoding";
import { getSubtle, pbkdf2Sha512, pbkdf2Sha512Noble, pbkdf2Sha512Subtle } from "./pbkdf2";
import {
getNodeCrypto,
getSubtle,
pbkdf2Sha512,
pbkdf2Sha512Noble,
pbkdf2Sha512NodeCrypto,
pbkdf2Sha512Subtle,
} from "./pbkdf2";
interface TestVector {
secret: Uint8Array;
@ -139,6 +146,25 @@ describe("pbkdf2", () => {
});
});
describe("pbkdf2Sha512NodeCrypto", () => {
it("works", async () => {
const nodeCrypto = await getNodeCrypto();
if (!nodeCrypto) pending("The crypto module is not available in this environment");
{
const { secret, salt, iterations, keylen, expected } = botanTest;
const hash = await pbkdf2Sha512NodeCrypto(nodeCrypto, 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 pbkdf2Sha512NodeCrypto(nodeCrypto, secret, salt, iterations, keylen);
expect(hash).withContext(`brycx tests index ${index}`).toEqual(expected);
}
});
});
describe("pbkdf2Sha512Noble", () => {
it("works", async () => {
{

View File

@ -2,6 +2,27 @@ 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 getNodeCrypto(): Promise<any | undefined> {
try {
const nodeCrypto = 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 nodeCrypto === "object" && Object.keys(nodeCrypto).length <= 1) {
return undefined;
}
return nodeCrypto;
} catch {
return undefined;
}
}
export async function getSubtle(): Promise<any | undefined> {
// From Node.js 15 onwards, webcrypto is available in globalThis.
// In version 15 and 16 this was stored under the webcrypto key.
@ -47,6 +68,33 @@ export async function pbkdf2Sha512Subtle(
);
}
/**
* Implements pbkdf2-sha512 using the Node.js crypro module (`import "crypto"`).
* This does not use subtle from [Crypto](https://developer.mozilla.org/en-US/docs/Web/API/Crypto).
*/
export async function pbkdf2Sha512NodeCrypto(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
nodeCrypto: any,
secret: Uint8Array,
salt: Uint8Array,
iterations: number,
keylen: number,
): Promise<Uint8Array> {
assert(nodeCrypto, "Argument nodeCrypto is falsy");
assert(typeof nodeCrypto === "object", "Argument nodeCrypto is not of type object");
assert(typeof nodeCrypto.pbkdf2 === "function", "nodeCrypto.pbkdf2 is not a function");
return new Promise((resolve, reject) => {
nodeCrypto.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,
@ -69,6 +117,11 @@ export async function pbkdf2Sha512(
if (subtle) {
return pbkdf2Sha512Subtle(subtle, secret, salt, iterations, keylen);
} else {
return pbkdf2Sha512Noble(secret, salt, iterations, keylen);
const nodeCrypto = await getNodeCrypto();
if (nodeCrypto) {
return pbkdf2Sha512NodeCrypto(nodeCrypto, secret, salt, iterations, keylen);
} else {
return pbkdf2Sha512Noble(secret, salt, iterations, keylen);
}
}
}