Author

Topic: Having troubles with DER signatures (Read 649 times)

legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
January 02, 2016, 11:12:28 PM
#3
Hmm... I don't think the problem is with the DER format as I just tried this:

Code:
#include
#include
#include
#include

using namespace std;

bool static IsValidSignatureEncoding(const std::vector &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 " << 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.
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
January 02, 2016, 10:24:00 PM
#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?
legendary
Activity: 1890
Merit: 1086
Ian Knowles - CIYAM Lead Developer
January 02, 2016, 05:51:17 AM
#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)?
Jump to: