c0mm4nd / dart-secp256k1

Fucking dart!!! Force me to write all deps!!!
https://pub.dev/packages/secp256k1
MIT License
12 stars 1 forks source link

cannot verify signature with secp256k1 javascript library #2

Closed zk-steve closed 3 years ago

zk-steve commented 3 years ago

I've tried for almost a week but to no avail. I compared the publiclic key and private key generated from your library with the ones from this library: https://www.npmjs.com/package/secp256k1 (version 4.0.2) along with the hashes, they are all the same. Only the signatures were different. Pls help me

c0mm4nd commented 3 years ago

Thanks for your report.

Why are you expect a same signature? Signatures are born from random numbers so should be different. Signature is not hashing.

on its rtf

  1. A random value modulo q, dubbed k, is generated. That value shall not be 0; hence, it lies in the [1, q-1] range. Most of the remainder of this document will revolve around the process used to generate k. In plain DSA or ECDSA, k should be selected through a random selection that chooses a value among the q-1 possible values with uniform probability.

If you found any abnormal behavior, like the sig generated on dart-secp256k1 cannot be verified (by secp256k1.ecdsaVerify) on secp256k1-node, please tell me.

zk-steve commented 3 years ago

So, what you mean is with the same input data, there will be different generated signatures?

zk-steve commented 3 years ago

I actually cannot verify it

zk-steve commented 3 years ago

I hashed the data and then signed on it so that I can guarantee there was no difference in the input data

c0mm4nd commented 3 years ago

try v0.2.1, it shall be fixed

zk-steve commented 3 years ago

First of all, thank you greatly for such a quick fix. Although the library in javascript now can verify the signatures, it seems quite unstable as it’s can only verify successfully 2-3 out of 5 requests.

c0mm4nd commented 3 years ago

Thanks for your report. But temporarily I cannot reproduce the unsuccessfully situation, could you please provide the a (privateKeyHex, messageHash, signatureHex) set to help me reproduce it.

zk-steve commented 3 years ago

I hope this’s enough for you 5F9C5E7F-D90B-441B-A700-4A5569354A0F B01C6E27-BB1C-4619-B596-90D1C0927E4D 8CA987F0-724A-4AD3-9E9D-96BC2429DD32

zk-steve commented 3 years ago

These are images that my teammate sent me. You can use any oct online tool to extract wanted keys, and then use base64.decode to get the hex

c0mm4nd commented 3 years ago

Let me share my tests.

This is a nodejs verify server:

var EC = require('elliptic').ec;
var ec = new EC('secp256k1');

function hex2U8Array(hexString) {
    return Uint8Array.from(Buffer.from(hexString, 'hex'))
}

// console.log(secp256k1.ecdsaVerify(
//     hex2U8Array('5f9f8ab5f16d278368e9b41f00c799bc8f5672a659e5004ea6e652efdbf16741c15a3cdfb49e9d66f825a7d82f2f0e7e77a2b24f8514c62686281faf17dd7aa8'),
//     hex2U8Array('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'),
//     hex2U8Array('043d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9ae613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f')))

// console.log(ecdsa.verify(
//     {x: BigInt('0x3d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9a'), y: BigInt('0xe613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f')},
//     {r: BigInt('0xa5d15b1d488df57ebd4c53fcb5cf1bb2acf5f7df9be6887b8272ff83f892d620'), s: BigInt('0xc27d13454cfd19c05d5dc4baa9eb71ea5212237547bba51a0e2a3a78ee8b5cd3')}, 
//     BigInt('0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9')))

// console.log('r', BigInt('0x3d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9a').toString())
// console.log('s', BigInt('0xe613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f').toString())

var key = ec.keyFromPublic('043d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9ae613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f', 'hex')
// console.log(key.verify(hex2U8Array('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'),
// {r: 'b4029492aa6305ab3461adf47aef64fd7bb29c98cd6e28cb7d8ba3eab7e46457', s: 'fbd46c4033700355873d481ccd055080d74f4dd06664f10f4be4d29d2fbbe2f1'}))

const express = require('express');
const { exit } = require('process');
const app = express()
const port = 3000

app.get('/:R/:S', (req, res) => {
    r = req.params['R']
    s = req.params['S']
    console.log(r+':'+s)
    if (key.verify(hex2U8Array('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'), {r: r, s: s})) {
        console.log('failed: '+r+':'+s)
        res.send('failed')
        exit()
    }
    res.send('ok')
})

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

and this is the dart client:

import 'dart:convert';
import 'dart:io';

import 'package:secp256k1/secp256k1.dart';
import 'package:secp256k1/src/base.dart';

Future<void> main() async {
  for (var i = 0; i < 1000; i++) {
    var msgHash = getPrivKeyByRand(BigInt.one).toRadixString(16);
    var pk = PrivateKey.generate();
    var sig = pk.signature(msgHash);
    var pub = pk.publicKey;
    var ok = sig.verify(pub, msgHash);
    if (!ok) {
      throw Error();
    }
    var client = HttpClient();
    var rs = sig.toHexes();
    var r = rs[0];
    var s = rs[1];
    var req = await client.getUrl(Uri.parse('http://127.0.0.1:3000/${r}/${s}'));
    var res = await req.close();
    await res.transform(Utf8Decoder()).listen(print);
  }
}

no failure no exit in more than 10,000 times tests.

By the way, when I using secp256k1-node, it seems read the hex string in DER encoding, but in dart it is just a join of two 64 length hex string. So I choose its dep, elliptic lib, whose input parameters are more explicit, for testing.

zk-steve commented 3 years ago

hey, your example is actually returning success when your verify function return false

c0mm4nd commented 3 years ago

Sorry, I was so sleepy that made so many errors on test. These are the new working pair:

var EC = require('elliptic').ec;
var ec = new EC('secp256k1');

function hex2U8Array(hexString) {
    return Uint8Array.from(Buffer.from(hexString, 'hex'))
}

var key = ec.keyFromPublic('043d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9ae613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f', 'hex')
const express = require('express');
const { exit } = require('process');
const app = express()
const port = 3000

const hello_world = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'

app.get('/:R/:S', (req, res) => {
    let r = req.params['R']
    let s = req.params['S']
    console.log(r+':'+s)
    let ok = key.verify(hex2U8Array(hello_world), {r: r, s: s})
    if (!ok) {
        console.log(ok, r+':'+s)
        res.send('failed')
        exit()
    }
    res.send('ok')
})

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})
import 'dart:convert';
import 'dart:io';

import 'package:secp256k1/secp256k1.dart';

Future<void> main() async {
  var hello_world =
      'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
  for (var i = 0; i < 1000; i++) {
    var msgHash = hello_world; //getPrivKeyByRand(BigInt.one).toRadixString(16);
    var pk = PrivateKey.fromHex(
        'e91c15d2353314e8be46658e0071b794a2404b35a323200a1a320b5670250110');
    var sig = pk.signature(msgHash);
    var pub = pk.publicKey;
    assert(pub.toHex() ==
        '043d86c4708eaccc71e7c60eab06cf64b54d0d79b422c4dfac938436cdbb93db9ae613afe574d522ab2637f3f0b77ec2f211c4a526db3a7b4da1a2d1ec02a8ea9f');
    var ok = sig.verify(pub, msgHash);
    if (!ok) {
      throw Error();
    }
    var client = HttpClient();
    var rs = sig.toHexes();
    var r = rs[0];
    var s = rs[1];
    var req =
        await client.getUrl(Uri.parse('http://127.0.0.1:3000/' + r + '/' + s));
    var res = await req.close();
    await res.transform(Utf8Decoder()).listen(print);
  }
}
zk-steve commented 3 years ago

It worked with your provided JavaScript library

c0mm4nd commented 3 years ago

That is a part of secp256k1-node. On https://www.npmjs.com/package/secp256k1 's README

This module provides native bindings to bitcoin-core/secp256k1. In browser elliptic will be used as fallback.

zk-steve commented 3 years ago

It still bugs me why I couldn't verify it with secp256k1.ecdsaVerify

zk-steve commented 3 years ago

I notice that they use 32 bits for r and s instead of 64 bits

zk-steve commented 3 years ago

Can toCompressedHex solve the problem?

c0mm4nd commented 3 years ago

r & s are both 32 bytes(not bits) in dart-secp256k1.

I think you can try with these steps https://github.com/cryptocoinjs/secp256k1-node/blob/master/lib/elliptic.js#L332 , and inspect the params of ec.verify

zk-steve commented 3 years ago

My bad 👍

tibfox commented 3 years ago

@sonntuet1997 did you ever got it to work?

zk-steve commented 3 years ago

Yes, just change the server library to the above-mentioned and it worked!

@sonntuet1997 did you ever got it to work?

tibfox commented 3 years ago

oh well okay thx @sonntuet1997 I dont have the possibility to change the server side unfortunately but have random wrong signatures. Will dive deeper with the developer of the other side and open a new issue if needed.