TransWikia.com

Bitcoind does not like ECDSA (r, s) pair produced by OpenSSL

Bitcoin Asked by Tedy S. on February 9, 2021

I am writing transactions manually and have stumbled across a rather bizarre situation.

Only one in a few of the transactions I broadcast to bitcoind is accepted, otherwise I get a REJECT_NONSTANDARD (Non-canonical DER signature).

So I got my hands dirty and tracked the rejection to be originating from this line: https://github.com/bitcoin/bitcoin/blob/9c5f0d542d1db507b3b9c87bd9de6d0d758d51c1/src/script/interpreter.cpp#L163

I read about DER encoding and checked how IsValidSignatureEncoding is enforcing it, but I do not know why OpenSSL generates not-DER-compliant (r, s) values?

How should I overcome this? I am thinking of something along the lines of (pseudocode):

Pair (r, s);
do
{
   (r, s) = sign(hash, pvtkey);
} while (r[0] >= 128 || s[0] >= 128); // where r[0], s[0] should be the very first byte of each value

But isn’t that kind of redundant? Can I give OpenSSL any flag to produce a valid DER (R, S) pair in the first place?

3 Answers

The third byte encodes information about the content length. If content length is less than 127 bytes third byte is equal to content length. If it is greater than 127 and less than 255, the third byte is 0x81. If it is greater than 255, the third byte is 0x82.

The next byte(s) indicates the content length.

Reference: https://github.com/openssl/openssl/blob/master/crypto/asn1_dsa.c encode_der_length https://www.itu.int/rec/T-REC-X.690-201508-I/en

Answered by garima on February 9, 2021

The following python code can create a valid DER encoded signature given r and s as byte objects:

def ser_sig_der(r, s):
    sig = b"x30"

    # Make r and s as short as possible
    ri = 0
    for b in r:
        if b == "x00":
            ri += 1
        else:
            break
    r = r[ri:]
    si = 0
    for b in s:
        if b == "x00":
            si += 1
        else:
            break;
    s = s[si:]

    # Make positive of neg
    first = r[0]
    if first & (1 << 7) != 0:
        r = b"x00" + r
    first = s[0]
    if first & (1 << 7) != 0:
        s = b"x00" + s

    # Write total length
    total_len = len(r) + len(s) + 4
    sig += struct.pack("B", total_len)

    # write r
    sig += b"x02"
    sig += struct.pack("B", len(r))
    sig += r

    # write s
    sig += b"x02"
    sig += struct.pack("B", len(s))
    sig += s
    return sig

It is important to note that signatures in Bitcoin also contain an extra byte appended to the DER encoded signature which represents the sighash type. You will need to add that byte yourself.

Answered by Andrew Chow on February 9, 2021

A possible reason is that Bitcoin Core expects a low S value. Try changing s with N-s if s > N/2 (N is the curve order).

Source here

Answered by Mike D on February 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP