I follow some online code snippet practicing to do ECDSA sign operation as below.
const { privKey, pubKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' });
const sign = crypto.createSign('SHA256');
sign.update('message to be signed');
sign.end();
const signature = sign.sign(privKey);
const verify = crypto.createVerify('SHA256');
verify.update('message to be signed');
verify.end();
console.log(verify.verify(pubKey, signature));
But I have a question. How do I extract R S values from signature object? From the node.js doc[1], it mentions the default format is DER encoded ASN1. But checking online, some info like [2] talks about how to extract R S values, but it's neither node.js, nor talking about which position to slice the bytes array. So I am confused, how should I extract R S values from node.js Sign.sign() returned object.
Any advice? Many thanks.
[1]. https://nodejs.org/api/crypto.html#signsignprivatekey-outputencoding
In this post you can find a description of the ASN.1/DER and IEEE P1363 format for ECDSA signatures. This can be used to manually convert between the two formats.
However, a manual conversion is not required at all, as the crypto module also supports the IEEE P1363 format (r|s
) since v13.2.0, s. sign.sign()
. For this, the specifier dsaEncoding
is used and the IEEE P1363 format is selected with 'ieee-p1363'
.
The following shows the modification of your code so that it uses the IEEE P1363 format:
const crypto = require('crypto')
const { privateKey: privKey, publicKey: pubKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' });
const sign = crypto.createSign('SHA256');
sign.update('message to be signed');
sign.end();
const signature = sign.sign({key: privKey, dsaEncoding: 'ieee-p1363'});
console.log(signature.toString('hex'));
const verify = crypto.createVerify('SHA256');
verify.update('message to be signed');
verify.end();
console.log(verify.verify({key: pubKey, dsaEncoding: 'ieee-p1363'}, signature));
signature
now contains the signature as a concatenation of r
and s
: r|s
. r
and s
each have the length of the order of the generator point, padded with leading 0x00
values if required. For secp256k1, both values are 32 bytes each.