Closed zk-steve closed 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
- 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.
So, what you mean is with the same input data, there will be different generated signatures?
I actually cannot verify it
I hashed the data and then signed on it so that I can guarantee there was no difference in the input data
try v0.2.1, it shall be fixed
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.
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.
I hope this’s enough for you
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
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.
hey, your example is actually returning success when your verify function return false
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);
}
}
It worked with your provided JavaScript library
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.
It still bugs me why I couldn't verify it with secp256k1.ecdsaVerify
I notice that they use 32 bits for r and s instead of 64 bits
Can toCompressedHex solve the problem?
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
My bad 👍
@sonntuet1997 did you ever got it to work?
Yes, just change the server library to the above-mentioned and it worked!
@sonntuet1997 did you ever got it to work?
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.
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