2

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?

  • Are you using OpenSSL to encode the DER or did you write your own DER encoding function? It's very easy to end up with a function that is only correct for some inputs. – G. Maxwell Nov 18 '18 at 17:56
1

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

  • Actually that was not my initial issue, that's a completely different error code which I am getting now (SCRIPT_ERR_SIG_HIGH_S) after prepending my faulty S value with an 0x00 byte (I read in some other thread that should be done if the initial S's first byte is >= 128). So yeah most likely what I have to do now is what you suggested – Tedy S. Oct 18 '18 at 11:39
0

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.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.