Open tibfox opened 3 years ago
Reason: S value (the signature.s
) sometimes larger than ec.nh
(= ec.n.ushrn(1)
)
However, in require('elliptic').ec('secp256k1')
there is no such limitation on signature.s
.
The solution is to add this when signaturing, but does any document about why doing this?
Oh thanks for the quick response! Unfortunately changing the server side code is not an option for me so the solution you provided is not executable in my case. And additionally to this I'm not able / knowledged enough to correctly execute the solution on the dart side. Would you be so kind to point out how I could implement this in dart? Sorry but I'm quiet new to cryptographics and was amazed to find your library bc it will make my life so much easier
found the reason -> the RFC6979 sign requires s is larger than half n (the nh)
I will write RFC6979 sign into package ecdsa first, then export RFC6979 sign on re-structuring this package
Great thank you so much!
@C0MM4ND do you have any time plan for implementing the changes? I am stucked a bit with my current project due to this error. No pressure - just asking
https://pub.dev/packages/ecdsa/versions/0.0.2
dart signer in test
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:ecdsa/ecdsa.dart';
import 'package:elliptic/elliptic.dart';
Future<void> main() async {
var ec = getS256();
var random = Random.secure();
var client = HttpClient();
for (var i = 0; i < 100000; i++) {
var msgHash = List<int>.generate(32, (i) => random.nextInt(256));
//.join(); // random 32 bytes;
var pk = ec.generatePrivateKey();
var sig = deterministicSign(pk, msgHash);
var pub = pk.publicKey;
var ok = verify(pub, msgHash, sig);
if (!ok) {
throw Error();
}
// var rs = sig.toHexes();
// print('testing: ' + pk.D.toRadixString(16));
var r = sig.R.toRadixString(16).padLeft(64, '0'); // rs[0];
var s = sig.S.toRadixString(16).padLeft(64, '0'); // rs[1];
var req = await client.getUrl(Uri.parse('http://127.0.0.1:3000/' +
[
pub,
List<String>.generate(32,
(index) => msgHash[index].toRadixString(16).padLeft(2, '0'))
.join(),
r,
s
].join('/')));
var res = await req.close();
res.transform(Utf8Decoder()).listen(print);
}
}
nodejs validator in test
const { exception } = require('console');
const express = require('express');
const { exit } = require('process');
const app = express()
const port = 3000
var ec = require('secp256k1/elliptic');
// var ec = new EC('secp256k1');
function hex2U8Array(hexString) {
return Uint8Array.from(Buffer.from(hexString, 'hex'))
}
app.get('/:pub/:hash/:R/:S', (req, res) => {
let pub = req.params['pub']
let hash = req.params['hash']
let r = req.params['R']
let s = req.params['S']
// console.log(r+':'+s)
console.log(hash)
// var key = ec.keyFromPublic(pub, 'hex')
var key = Buffer.from(pub, 'hex')
var sig = Buffer.from(r+s, 'hex')
var msg = Buffer.from(hash, 'hex')
try {
let ok = ec.ecdsaVerify(sig, msg, key)
if (!ok) {
console.log(ok, r+':'+s)
res.send('failed')
exit()
}
res.send('ok')
} catch (e) {
console.log('failed', r+':'+s)
res.send('failed'+ e.message)
exit()
}
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Passed more than 10,000 tests.
unfortunately now the signature is not the correct length anymore. Before I implemented the change it always had the correct length but wasn't valid randomly. What am I doing wrong now?
Expected signature to be an Uint8Array with length 64
is what I get back from the server.
This is what I do to sign:
Future<Transaction> sign(
Transaction tx, String applicationUser, String privKey) async {
var ec = getS256();
Transaction txNew = Transaction(type: tx.type, data: tx.data);
txNew.sender = applicationUser; // set sender of tx
txNew.ts = DateTime.now().millisecondsSinceEpoch; // set timestamp
var jsonString = jsonEncode(txNew.toJson());
List<int> txBytes = utf8.encode(jsonString);
Digest digest = sha256.convert(txBytes);
var messageHash = digest.toString();
txNew.hash = messageHash;
String pkHex = HEX.encode(base58.decode(privKey));
PrivateKey _pk = PrivateKey.fromHex(ec, pkHex);
Signature _sig = deterministicSign(_pk, HEX.decode(messageHash));
String _sigHex = _sig.toString();
Uint8List sigBytes = Uint8List.fromList(HEX.decode(_sigHex));
print(sigBytes.length);
String _sigB58 = base58.encode(sigBytes);
txNew.signature = _sigB58;
return txNew;
}
In ecdsa package
_sig.toString()
equals to _sig.toDER()
(Distinguished Encoding Rules)
To get the compacted hex like package secp256k1, you need to use
String _sigHex = _sig.R.toRadixString(16).padLeft(64, '0') + // r's hex
_sig.S.toRadixString(16).padLeft(64, '0'); // s's hex
toCompactHex will be add into ecdsa package, but the toString wont be changed because the toDER/toASN1 is a standard defined in spec https://www.secg.org/sec2-v2.pdf
using ASN.1 syntax and specifies ASN.1 object identifiers for the elliptic curve domain parameters recommended in this document
Amazing thank you so much for the short and quick answer! It's working like a charm!
It really helped a lot. Thank you guys so much!!
hey there! I encounter the same issue like mentioned here: #2 And I can not find any reason for that.
The created signature from dart is marked as invalid from the nodejs side randomly. I know it is probably a pretty silly bug on my coding side but I would really like you to verify that both libraries can work together since I can not change any server side code and I am slowly goign nuts here.
this is how I create the signature in dart (I use flutter_bloc if that's helpful)
And this is a short and dirty nodejs part I reproduce the server side code to verify the signature:
Like I've said, it is random - no real schema I can observe. From 5 tries - 2 or less are valid.
Please let me know if you need any further examples or codelines. I am very thankful for your work and response in advanced.