bip39 icon indicating copy to clipboard operation
bip39 copied to clipboard

Replace create hash with sha.js

Open webmaster128 opened this issue 2 years ago • 8 comments

Based on #172 (merge #172 first).

Closes #170

Turns out this is not enought to get create-hash out of the dependency tree because pbkdf2 uses it. However, it is possible to implement pbkdf2 manually or get that fixed in the next step.

webmaster128 avatar Dec 06 '21 10:12 webmaster128

Made a quick dirty impl of pbkdf2 using sha.js with SHA512, 64 dlen, and 2048 iterations hard coded. It was 5x~6x slower :-|

... we should probably section this off to a browser section... People in Node should not have to suffer even a 2x slowdown because of the browser.

import * as sha from 'sha.js';

const sha512 = (data: Buffer | string): Buffer => {
  return sha('sha512')
    .update(data)
    .digest();
};

const xorBuf = (a: Buffer, b: Buffer): Buffer => {
  const ret = Buffer.allocUnsafe(a.length);
  for (let i = 0; i < a.length; i++) {
    ret[i] = a[i] ^ b[i];
  }
  return ret;
};

const hmac = (data: Buffer | string, key: Buffer | string): Buffer => {
  const k = key.length > 128 ? sha512(key) : Buffer.from(key as any, 'utf8');
  const buf = Buffer.alloc(128);
  k.copy(buf);
  const oKey = Buffer.from(buf);
  const iKey = Buffer.from(buf);
  for (let i = 0; i < 128; i++) {
    oKey[i] = oKey[i] ^ 0x5c;
    iKey[i] = iKey[i] ^ 0x36;
  }
  return sha512(
    Buffer.concat([
      oKey,
      sha512(Buffer.concat([iKey, Buffer.from(data as any, 'utf8')])),
    ]),
  );
};

const ITERS = 2048;

export function pbkdf2(
  password: Buffer | string,
  salt: Buffer | string,
): Buffer {
  const UArray = [];
  UArray.push(
    hmac(
      Buffer.concat([
        Buffer.from(salt as any, 'utf8'),
        Buffer.from([0, 0, 0, 1]),
      ]),
      password,
    ),
  );
  let ret = UArray[0];
  for (let i = 1; i < ITERS; i++) {
    const hash = hmac(UArray[i - 1], password);
    UArray.push(hash);
    ret = xorBuf(ret, hash);
  }
  return ret;
}

junderw avatar Dec 06 '21 15:12 junderw

... we should probably section this off to a browser section... People in Node should not have to suffer even a 2x slowdown because of the browser.

I doubt "browsers" is the right category. JS environments with/without Crypto APIs is probably the better classification. SubtleCrypto is available in Node.js 15.

It should not be too hard to come up with a pbkdf2 implementation that uses

  1. SubtleCrypto
  2. Node.js crypto module
  3. Pure-JS fallback

as long as only SHA2 is needed.

webmaster128 avatar Dec 06 '21 15:12 webmaster128

I am not comfortable raising the requirement of NodeJS above v14 right now, so SubtleCrypto for Node is not possible.

junderw avatar Dec 06 '21 15:12 junderw

Maybe after v12 EOL 2022-04-30

junderw avatar Dec 06 '21 15:12 junderw

Sure, me neither. But you can have an implementation that uses 1., 2. or 3. depending on availability.

webmaster128 avatar Dec 06 '21 15:12 webmaster128

So my current thinking given your input is reimplementing pbkdf2

  • in TypeScript
  • supporting SHA2 only
  • targeting reasonabily modern JS environments(native async/await and Uint8Array support)
  • removal of the sync variant
  • using SubtleCrypto, Node.js crypto module and pure-JS fallback depending on availability
  • using the same SHA2 implementation for pbkdf2 and deriveChecksumBits

webmaster128 avatar Dec 06 '21 15:12 webmaster128

Any chance this change will be merged?

infodusha avatar Sep 12 '22 16:09 infodusha

I'm not planning to continue working on this. But feel free to use my commits if you want to finalize it.

webmaster128 avatar Sep 12 '22 16:09 webmaster128

not relevant anymore https://github.com/bitcoinjs/bip39/blob/a7ecbfe2e60d0214ce17163d610cad9f7b23140c/package.json#L38

paulmillr avatar Mar 22 '24 14:03 paulmillr