Signatures

Cryptographic signatures in Bitcoin are done using an elliptic curve. Specifically the secp256k1 variant of standard/known curves. Consequently all signatures employ the Elliptic Curve Diffie-Hellman Signature Algorithm (ECDSA) with SHA256 hashes.

In general, one can sign the hash of an arbitrary piece of data using ECDSA. In bitcoin the signatures are typically generated using the data in a transaction. You can refer to the section on Signature Schemes from the Developer Guide to learn more about exactly which parts of a transaction get signed.

The SVSignature() class is our primary means of directly working with signatures. It has two primary modes of operation:

  • Verification-Only Mode. Wherein an instance of the class can only verify signatures.
  • Signing Mode. Wherein an instance of the class can be used to generate signatures.

Signatures in Bitcoin Transaction Format

Bitcoin’s internal signature verification algorithm ( CHECK_SIG ), does not work with standard DER-encoded ECDSA signatures. Instead, a special encoding format is used. This encoding format consists of a standard DER-encoded ECDSA signature, with an additional byte appended.

The extra byte at the end is a flag that indicates which of the SIGHASH algorithms were used in constructing the signature.

Reconstruct a Signature from “Transaction-Format”


/* for more complete example have a look at the unit tests
https://github.com/twostack/dartsv/blob/master/test/crypto/signature_test.dart
*/

var buffer = '30450221008bab1f0a2ff2f9cb89...'; //truncated for brevity
var sig = SVSignature.fromTxFormat(buffer);

Get “Transaction-Format” of a Signature

//create an SVSignature() instance used for signing
var privateKey = SVPrivateKey();

//create signature instance
var sig = SVSignature.fromPrivateKey(privateKey);

//sign a message
var hexMessage = HEX.encode(Utf8Encoder().convert("some arbitrary data"));
sig.sign(hexMessage);

//get tx format
var txFormat = sig.toTxFormat();

Signatures in DER Format

Create DER-formatted Signature

To obtain standard DER-formatted signatures, do the following:


//create a signature from (r,s) components
var r = BigInt.parse('63173831029936981022572627018246571655303050627048489594159321588908385378810');
var s = BigInt.parse('4331694221846364448463828256391194279133231453999942381442030409253074198130');

var sig = SVSignature.fromECParams(r, s);

//create a hexadecimal string of the DER-encoded signature
var derHex = HEX.encode(sig.toDER());

Check if Signature is in DER format

Use the HEX-encoded DER text string for checking…

var sighex = '3042021e17cfe77536c3fb052...'; //truncated for brevity
var isDerFormat = SVSignature.isTxDER(sighex);

Signatures in Compact Format

Compact format is a special way of constructing a string representation of a signature, which allows for the public key to be recovered from the signature.

When we recover a public key from a signature, the SVSignature() instance can be used for signature verification only

This paper (secion 4.1.6) describes an algorithm for recovering the public key from an ECDSA signature: (http://www.secg.org/sec1-v2.pdf)

One needs:

  • [ a buffer] - Signature in Compact Signature form

  • [a signed message] - Message signed with the signature in [buffer]

Create a compact format Signature

//create random private key
var privateKey = SVPrivateKey();

//create a SVSignature() instance used for signing
var sig = SVSignature.fromPrivateKey(privateKey);

//sign a message
var hexMessage = HEX.encode(Utf8Encoder().convert("some arbitrary data"));
var derSig = sig.sign(hexMessage, forCompact: true);

var compactSig = HEX.encode(sig.toCompact());

Reconstruct a signature from Compact format

This method will automatically recover the public key as well.


//reconstruct the SVSignature() instance
var sig = SVSignature.fromCompact(HEX.decode(compactSig), HEX.decode(hexMessage));

//retrieve the public key
SVPublicKey pubKey = sig.publicKey;

Signing and Verification

Signing

//create an SVSignature() instance used for signing
var privateKey = SVPrivateKey();

//create signature instance
var sig = SVSignature.fromPrivateKey(privateKey);

//sign a message
var hexMessage = HEX.encode(Utf8Encoder().convert("some arbitrary data"));
sig.sign(hexMessage);

//get tx format
var txFormat = sig.toTxFormat();

//get a DER-encoded byte array
var derBuffer = sig.toDER()

//get a HEX-encode DER string
var sigDERHex = sig.toString()

Verification

NOTE: See Create a compact format Signature

The following code will perform an implicit public key recovery for signature verification purposes. It is also possible to explicitly initialise a SVSignature() instance using a Public Key.

//reconstruct the SVSignature() instance
var sig = SVSignature.fromCompact(HEX.decode(compactSig), HEX.decode(hexMessage));

//internally the SVSignature() instance already has a copy of the public key

var hexMessage = HEX.encode(Utf8Encoder().convert("some arbitrary data"));

val isValidSig = sig.verify(hexMessage);