paulmillr / noble-secp256k1

Fastest 4KB JS implementation of secp256k1 signatures and ECDH
https://paulmillr.com/noble
MIT License
757 stars 114 forks source link

Backwards compatbility with secp256k1-node #89

Closed tre-dev closed 1 year ago

tre-dev commented 1 year ago

I'm trying to replace the secp256k1 npm package (v3) with this one, but am running into issues where the signSync result is different.

Original code:

  public sign(message: Buffer): Signature {
    let rv: { signature: Buffer; recovery: number }
    let attempts = 0
    do {
      const options = {
        data: sha256(Buffer.concat([message, Buffer.alloc(1, ++attempts)]))
      }
      rv = secp256k1.sign(message, this.key, options)
    } while (!isCanonicalSignature(rv.signature))
    return new Signature(rv.signature, rv.recovery)
  }

(link to code repo: https://gitlab.syncad.com/hive/dhive/-/blob/master/src/crypto.ts#L266-280)

Noble code:

    public sign(message: Uint8Array): Signature {
        let rv: { signature: Uint8Array; recid: number };
        let attempts = 0;
        do {
            const a = new Uint8Array(1);
            a[0] = ++attempts;
            const { signature, recid } = ecdsaSign(hash.sha256(new Uint8Array([...message, ...a])), this.key);
            rv = { signature: Uint8Array.from(signature), recid };
        } while (!keyUtils.isCanonicalSignature(rv.signature));
        return new Signature(rv.signature, rv.recid);
    }

 export function ecdsaSign(msgHash: Uint8Array, privateKey: Uint8Array, extraEntropy?: Hex | true, out?: Output): Signature {
    assertBytes(msgHash, 32);
    assertBytes(privateKey, 32);

    const [signature, recid] = secp.signSync(msgHash, privateKey, {
        recovered: true,
        der: false,
        extraEntropy,
        canonical: false,
    });
    return { signature: output(out, 64, signature), recid };
}

I took the ecdsaSign function from your ethereum-js lib and modified it to fit my use-case. But my guess is that the extraEntropy is handled different within secp256k1. Below is the code that handles the nouncefn & extraData.

https://github.com/cryptocoinjs/secp256k1-node/blob/v3.x/lib/js/index.js#L140-L175

Please let me know if the provided code is enough for you to debug this. Thank you!

paulmillr commented 1 year ago

Hi.

Not interested in debugging this. You should switch fully to noble instead of using legacy libraries and compat layers. Our extraEntropy is fully compatible with C libsecp256k1 BTC, for example.

tre-dev commented 1 year ago

Hey, I'm actually only using noble libraries for hashing. Was setting up a clean example and was able to fix it. Thought I tried that before, but maybe something else was the problem when I did that. Anyway, here's the fix in my code:

const h = hash.sha256(new Uint8Array([...message, ...a]));
const { signature, recid } = ecdsaSign(message, key, h);

OT question: the code is still relying on ByteBuffer. (bytebuffer.js) Do you have any thoughts on how to best replace it?

paulmillr commented 1 year ago

bytebuffer is basically uint8array with a few fancy methods. replace those methods and that should be it.