Bitcoin Forum
May 08, 2024, 07:05:57 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Having troubles with DER signatures  (Read 629 times)
CIYAM (OP)
Legendary
*
Offline Offline

Activity: 1890
Merit: 1078


Ian Knowles - CIYAM Lead Developer


View Profile WWW
January 02, 2016, 10:51:17 AM
Last edit: January 03, 2016, 03:46:18 AM by CIYAM
 #1

This is the code I've written for creating Bitcoin signatures: https://github.com/ciyam/ciyam/blob/master/src/crypto_keys.cpp#L513 (the code was originally from Bitcoin itself and is using OpenSSL).

It had been working fine (and I also added code to ensure low S values) but now whilst I am trying to test a CLTV tx I am always getting the following error when attempting to send a transaction:

Code:
error: {"code":-26,"message":"16: mandatory-script-verify-flag-failed (Non-canonical DER signature)"}

This is a sample DER encoded signature:

Code:
3045022100da3114f49f3135fa0e3723a2c05ec304f4d16ce3e3f11920e7caa296dd53a9e202206870c44c3681bb9339c1233895a86c6b2861e3d6e6fa562601accae47fc445b201

I do see in the above that there is 00 after the 21 length and am wondering if the zero padding is the problem (the comments I read in the latest Bitcoin code seem to indicate that leading zeroes should not be there unless the value is negative - hmm... but isn't that number negative?).

If so what should I change in my code to make sure that the DER signature is being canonically constructed (i.e. is it even possible to be done correctly using OpenSSL or do I need to write code to get rid of the leading zeros myself)?

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
1715195157
Hero Member
*
Offline Offline

Posts: 1715195157

View Profile Personal Message (Offline)

Ignore
1715195157
Reply with quote  #2

1715195157
Report to moderator
Bitcoin mining is now a specialized and very risky industry, just like gold mining. Amateur miners are unlikely to make much money, and may even lose money. Bitcoin is much more than just mining, though!
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
CIYAM (OP)
Legendary
*
Offline Offline

Activity: 1890
Merit: 1078


Ian Knowles - CIYAM Lead Developer


View Profile WWW
January 03, 2016, 03:24:00 AM
Last edit: January 03, 2016, 03:42:42 AM by CIYAM
 #2

Here is another example of a DER signature that is being rejected:

Code:
3044022069c288dd4f1995d6f95c6338842dd55934b7ac78c3bae65950ec64e7477cd5c902203fb73d7c329ec4f3da2f403eddc635215673cb13487f0e7e803969488678565001

There are no leading zeroes in this one - so what exactly is wrong with it?

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
CIYAM (OP)
Legendary
*
Offline Offline

Activity: 1890
Merit: 1078


Ian Knowles - CIYAM Lead Developer


View Profile WWW
January 03, 2016, 04:12:28 AM
Last edit: January 03, 2016, 06:39:28 AM by CIYAM
 #3

Hmm... I don't think the problem is with the DER format as I just tried this:

Code:
#include <string>
#include <vector>
#include <iomanip>
#include <iostream>

using namespace std;

bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
    // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
    // * total-length: 1-byte length descriptor of everything that follows,
    //   excluding the sighash byte.
    // * R-length: 1-byte length descriptor of the R value that follows.
    // * R: arbitrary-length big-endian encoded R value. It must use the shortest
    //   possible encoding for a positive integers (which means no null bytes at
    //   the start, except a single one when the next byte has its highest bit set).
    // * S-length: 1-byte length descriptor of the S value that follows.
    // * S: arbitrary-length big-endian encoded S value. The same rules apply.
    // * sighash: 1-byte value indicating what data is hashed (not part of the DER
    //   signature)

cout << "at 0..." << endl;
cout << "sig.size() = " << sig.size() << endl;
    // Minimum and maximum size constraints.
    if (sig.size() < 9) return false;
    if (sig.size() > 73) return false;

cout << "at 1..." << endl;
    // A signature is of type 0x30 (compound).
    if (sig[0] != 0x30) return false;

cout << "at 2..." << endl;
    // Make sure the length covers the entire signature.
    if (sig[1] != sig.size() - 3) return false;

cout << "at 3..." << endl;
    // Extract the length of the R element.
    unsigned int lenR = sig[3];

cout << "at 4..." << endl;
    // Make sure the length of the S element is still inside the signature.
    if (5 + lenR >= sig.size()) return false;

cout << "at 5..." << endl;
    // Extract the length of the S element.
    unsigned int lenS = sig[5 + lenR];

cout << "lenR = " << lenR << endl;
cout << "lenS = " << lenS << endl;
    // Verify that the length of the signature matches the sum of the length
    // of the elements.
    if ((size_t)(lenR + lenS + 7) != sig.size()) return false;
 
cout << "at 6..." << endl;
    // Check whether the R element is an integer.
    if (sig[2] != 0x02) return false;

cout << "at 7..." << endl;
    // Zero-length integers are not allowed for R.
    if (lenR == 0) return false;

cout << "at 8..." << endl;
    // Negative numbers are not allowed for R.
    if (sig[4] & 0x80) return false;

cout << "at 9..." << endl;
    // Null bytes at the start of R are not allowed, unless R would
    // otherwise be interpreted as a negative number.
    if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false;

cout << "at 10..." << endl;
    // Check whether the S element is an integer.
    if (sig[lenR + 4] != 0x02) return false;

cout << "at 11..." << endl;
    // Zero-length integers are not allowed for S.
    if (lenS == 0) return false;

cout << "at 12..." << endl;
    // Negative numbers are not allowed for S.
    if (sig[lenR + 6] & 0x80) return false;

cout << "at 13..." << endl;
    // Null bytes at the start of S are not allowed, unless S would otherwise be
    // interpreted as a negative number.
    if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false;

cout << "at 14..." << endl;
    return true;
}

int main( int argc, char* argv[ ] )
{
   if( argc != 2 )
      cout << "usage: x <DER sig>" << endl;
   else
   {
      string sig( argv[ 1 ] );

      vector< unsigned char > bytes;
      for( size_t i = 0; i < sig.size( ); i += 2 )
      {
         unsigned char ch = '\0';

         if( sig[ i ] == 'a' || sig[ i ] == 'A' )
            ch = 0xa0;
         else if( sig[ i ] == 'b' || sig[ i ] == 'B' )
            ch = 0xb0;
         else if( sig[ i ] == 'c' || sig[ i ] == 'C' )
            ch = 0xc0;
         else if( sig[ i ] == 'd' || sig[ i ] == 'D' )
            ch = 0xd0;
         else if( sig[ i ] == 'e' || sig[ i ] == 'E' )
            ch = 0xe0;
         else if( sig[ i ] == 'f' || sig[ i ] == 'F' )
            ch = 0xf0;
         else if( sig[ i ] >= '0' && sig[ i ] <= '9' )
            ch = ( sig[ i ] - '0' ) << 4;

         if( sig[ i + 1 ] == 'a' || sig[ i + 1 ] == 'A' )
            ch |= 0x0a;
         else if( sig[ i + 1 ] == 'b' || sig[ i + 1 ] == 'B' )
            ch |= 0x0b;
         else if( sig[ i + 1 ] == 'c' || sig[ i + 1 ] == 'C' )
            ch |= 0x0c;
         else if( sig[ i + 1 ] == 'd' || sig[ i + 1 ] == 'D' )
            ch |= 0x0d;
         else if( sig[ i + 1 ] == 'e' || sig[ i + 1 ] == 'E' )
            ch |= 0x0e;
         else if( sig[ i + 1 ] == 'f' || sig[ i + 1 ] == 'F' )
            ch |= 0x0f;
         else if( sig[ i + 1 ] >= '0' && sig[ i + 1 ] <= '9' )
            ch |= ( sig[ i + 1 ] - '0' );

         bytes.push_back( ch );
      }

      cout << IsValidSignatureEncoding( bytes ) << endl;
   }
}

And then ran this:
Code:
> x 3044022069c288dd4f1995d6f95c6338842dd55934b7ac78c3bae65950ec64e7477cd5c902203fb73d7c329ec4f3da2f403eddc635215673cb13487f0e7e803969488678565001

at 0...
sig.size() = 71
at 1...
at 2...
at 3...
at 4...
at 5...
lenR = 32
lenS = 32
at 6...
at 7...
at 8...
at 9...
at 10...
at 11...
at 12...
at 13...
at 14...
1

which tells me that there is nothing wrong with it. I'm guessing I have something else wrong with a script so that the item on the stack is not actually a signature.

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!