[softfork proposal] Strict DER signatures

[softfork proposal] Strict DER signatures
2015-01-21  4:45 ` Rusty Russell
                   ` (7 more replies)
  
From: Pieter Wuille @ 2015-01-21  0:35 UTC (permalink / raw)
  To: Bitcoin Dev

Hello everyone,

We've been aware of the risk of depending on OpenSSL for consensus
rules for a while, and were trying to get rid of this as part of BIP
62 (malleability protection), which was however postponed due to
unforeseen complexities. The recent evens (see the thread titled
"OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
on this mailing list) have made it clear that the problem is very
real, however, and I would prefer to have a fundamental solution for
it sooner rather than later.

I therefore propose a softfork to make non-DER signatures illegal
(they've been non-standard since v0.8.0). A draft BIP text can be
found on:

    https://gist.github.com/sipa/5d12c343746dad376c80

The document includes motivation and specification. In addition, an
implementation (including unit tests derived from the BIP text) can be
found on:

    https://github.com/sipa/bitcoin/commit/bipstrictder

Comments/criticisms are very welcome, but I'd prefer keeping the
discussion here on the mailinglist (which is more accessible than on
the gist).

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-21  4:45 ` 
                   ` 
  
From: Pieter Wuille @ 2015-01-21  0:35 UTC (permalink / raw)
  To: Bitcoin Dev

Hello everyone,

We've been aware of the risk of depending on OpenSSL for consensus
rules for a while, and were trying to get rid of this as part of BIP
62 (malleability protection), which was however postponed due to
unforeseen complexities. The recent evens (see the thread titled
"OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
on this mailing list) have made it clear that the problem is very
real, however, and I would prefer to have a fundamental solution for
it sooner rather than later.

I therefore propose a softfork to make non-DER signatures illegal
(they've been non-standard since v0.8.0). A draft BIP text can be
found on:

    https://gist.github.com/sipa/5d12c343746dad376c80

The document includes motivation and specification. In addition, an
implementation (including unit tests derived from the BIP text) can be
found on:

    https://github.com/sipa/bitcoin/commit/bipstrictder

Comments/criticisms are very welcome, but I'd prefer keeping the
discussion here on the mailinglist (which is more accessible than on
the gist).

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-21  4:45 ` Rusty Russell

  
From: Pieter Wuille @ 2015-01-21 16:49 UTC (permalink / raw)
  To: Rusty Russell;  Bitcoin Dev

On Tue, Jan 20, 2015 at 11:45 PM, Rusty Russell <rusty@rustcorp•com.au> wrote:
> // Null bytes at the start of R are not allowed, unless it would otherwise be
> // interpreted as a negative number.
>     if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80))
>     return false;
>
> You mean "null bytes at the start of S".

Thanks, fixed.

-- 
Pieter



 permalink raw reply	[|nested]
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille

  2015-01-21 16:49   ` Pieter Wuille
  2015-01-21 19:10 ` 
                   ` 
  7 siblings, 1 reply; 32+ messages in thread
From: Rusty Russell @ 2015-01-21  4:45 UTC (permalink / raw)
  To: Pieter Wuille, Bitcoin Dev

Pieter Wuille <pieter.wuille@gmail•com> writes:
> Hello everyone,
>
> We've been aware of the risk of depending on OpenSSL for consensus
> rules for a while, and were trying to get rid of this as part of BIP
> 62 (malleability protection), which was however postponed due to
> unforeseen complexities. The recent evens (see the thread titled
> "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> on this mailing list) have made it clear that the problem is very
> real, however, and I would prefer to have a fundamental solution for
> it sooner rather than later.
>
> I therefore propose a softfork to make non-DER signatures illegal
> (they've been non-standard since v0.8.0). A draft BIP text can be
> found on:
>
>     https://gist.github.com/sipa/5d12c343746dad376c80

Cut and paste bug in the last check:

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

You mean "null bytes at the start of S".

Cheers,
Rusty.



^ permalink raw reply	[|nested] 32+ messages in thread
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
  2015-01-21  4:45 ` Rusty Russell

  2015-01-21 19:29 ` 
                   ` 
  7 siblings, 0 replies; 32+ messages in thread
From: Peter Todd @ 2015-01-21 19:10 UTC (permalink / raw)
  To: Pieter Wuille, Gregory Maxwell;  Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 1533 bytes --]

On Tue, Jan 20, 2015 at 07:35:49PM -0500, Pieter Wuille wrote:

I read this and it's boring, now that all my objections have been met. :)

I'll try get a chance to actually test/review this in detail; in SF for
the next three weeks with some ugly deadlines and a slow laptop. :(

> Hello everyone,
> 
> We've been aware of the risk of depending on OpenSSL for consensus
> rules for a while, and were trying to get rid of this as part of BIP
> 62 (malleability protection), which was however postponed due to
> unforeseen complexities. The recent evens (see the thread titled
> "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> on this mailing list) have made it clear that the problem is very
> real, however, and I would prefer to have a fundamental solution for
> it sooner rather than later.
> 
> I therefore propose a softfork to make non-DER signatures illegal
> (they've been non-standard since v0.8.0). A draft BIP text can be
> found on:
> 
>     https://gist.github.com/sipa/5d12c343746dad376c80
> 
> The document includes motivation and specification. In addition, an
> implementation (including unit tests derived from the BIP text) can be
> found on:
> 
>     https://github.com/sipa/bitcoin/commit/bipstrictder
> 
> Comments/criticisms are very welcome, but I'd prefer keeping the
> discussion here on the mailinglist (which is more accessible than on
> the gist).

-- 
'peter'[:-1]@petertodd.org
00000000000000001a5e1dc75b28e8445c6e8a5c35c76637e33a3e96d487b74c

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 650 bytes --]

^ permalink raw reply	[|nested] 32+ messages in thread
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
  2015-01-21  4:45 ` Rusty Russell
  2015-01-21 19:10 ` Peter Todd

  2015-01-21 20:30   ` Pieter Wuille
  2015-01-21 20:37   ` Gavin Andresen
  2015-01-21 20:27 ` 
                   ` 
  7 siblings, 2 replies; 32+ messages in thread
From: Douglas Roark @ 2015-01-21 19:29 UTC (permalink / raw)
  To: Bitcoin Dev

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

On 2015/1/20 19:35, Pieter Wuille wrote:> Hello everyone,
> Comments/criticisms are very welcome, but I'd prefer keeping the 
> discussion here on the mailinglist (which is more accessible than
> on the gist).

Nice paper, Pieter. I do have a bit of feedback.

1)The first sentence of "Deployment" has a typo. "We reuse the
double-threshold switchover mechanism from BIP 34, with the same
*thresholds*, [....]"

2)I think the handling of the sighash byte in the comments of
IsDERSignature() could use a little tweaking. If you look at
CheckSignatureEncoding() in the actual code (src/script/interpreter.cpp
in master), it's clear that the sighash byte is included as part of the
signature struct, even though it's not part of the actual DER encoding
being checked by IsDERSignature(). This is fine. I just think that the
code comments in the paper ought to make this point clearer, either in
the sighash description, or as a comment when checking the sig size
(i.e., size-3 is valid because sighash is included), or both.

3)The paper says a sig with size=0 is correctly coded but is neither
valid nor DER. Perhaps this code should be elsewhere in the Bitcoin
code? It seems to me that letting a sig pass in IsDERSignature() when
it's not actually DER-encoded is incorrect.

Thanks.

- ---
Douglas Roark
Senior Developer
Armory Technologies, Inc.
doug@bitcoinarmory•com
PGP key ID: 92ADC0D7
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
Comment: GPGTools - https://gpgtools.org

iQIcBAEBCgAGBQJUv/4vAAoJEGybVGGSrcDXMxkP/1N2lLAloCKdRUpMBLPEZ5jh
bJ4reCeqrMy6JetsKSGfGKdAe7kGkeRl6s8dlHYnpUmnODXU9BCku3zHi3+qm8IC
GZlwSdSSgmRneP7btPula0CG31o7X2UJiDW/2IOZl6ul8b7LB2L56O+Ew+PNm+at
tCfRcpKtq9LYCnRYR0azd4c5YY9/o7zlkpGi8CututzuEa4Rcm92U1extoo2tC/j
nzUfbfcQVL0a7JaRU4VYNceYrcG/xSpKPjsEU/F+5IwnUxL/kebz0EDt1kzm+fOE
EMUMXyYgoyW5VDFNjxu00PnJUfVNCOXN/N/h9eCdskCL3AtH6xg1kzam5OGvpEZS
QDMNSmQl4Zpx5WiATylNkhhzb/8GowamkSFg4SUjBsjpwOTMTIF0Qhnt+DdzwpI2
etxCGds154nL4p/bkulseczwxOZWin9oZxJnCxp40oFl8fva0BwHVx45uMyI61Ko
qRJ9Ol0CDoId3h1EMTt4uyoNxrOzgrj8/+V4BBytOAMMmsfD0VgY68xzdywJxYnC
jgU99huhwtJpn9QT6JAbgPAaboomu6hDCohV+J+DCCkIiYFk1jxp+FQ4xZDzcKeo
gMYpmFefPAxnHvDXf1v1A+Xw8plN6/NREaIpprh7Ep+q/8vYAiwwHfKjubdMkB3D
WnTR5YbqyGxc/Pvh9Ncq
=C/wj
-----END PGP SIGNATURE-----



^ permalink raw reply	[|nested] 32+ messages in thread
2015-01-21 19:29 ` Douglas Roark

  2015-01-21 20:39     ` Douglas Roark
  2015-01-21 20:37   ` Gavin Andresen
  
From: Pieter Wuille @ 2015-01-21 20:30 UTC (permalink / raw)
  To: Douglas Roark;  Bitcoin Dev

On Wed, Jan 21, 2015 at 2:29 PM, Douglas Roark <doug@bitcoinarmory•com> wrote:
> Nice paper, Pieter. I do have a bit of feedback.

Thanks for the comments. I hope I have clarified the text a bit accordingly.

> 1)The first sentence of "Deployment" has a typo. "We reuse the
> double-threshold switchover mechanism from BIP 34, with the same
> *thresholds*, [....]"

Fixed.

> 2)I think the handling of the sighash byte in the comments of
> IsDERSignature() could use a little tweaking. If you look at
> CheckSignatureEncoding() in the actual code (src/script/interpreter.cpp
> in master), it's clear that the sighash byte is included as part of the
> signature struct, even though it's not part of the actual DER encoding
> being checked by IsDERSignature(). This is fine. I just think that the
> code comments in the paper ought to make this point clearer, either in
> the sighash description, or as a comment when checking the sig size
> (i.e., size-3 is valid because sighash is included), or both.

I've renamed the function to IsValidSignatureEncoding, as it is not
strictly about DER (it adds a Bitcoin-specific byte, and supports and
empty string too).

> 3)The paper says a sig with size=0 is correctly coded but is neither
> valid nor DER. Perhaps this code should be elsewhere in the Bitcoin
> code? It seems to me that letting a sig pass in IsDERSignature() when
> it's not actually DER-encoded is incorrect.

I've expanded the comments about it a bit.

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-21 19:29 ` Douglas Roark
  2015-01-21 20:30   ` Pieter Wuille

  2015-01-21 20:52     ` Douglas Roark
  2015-01-21 21:22     ` Pieter Wuille
  
From: Gavin Andresen @ 2015-01-21 20:37 UTC (permalink / raw)
  To: Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 771 bytes --]

DERSIG BIP looks great to me, just a few nit-picky changes suggested:

You mention the "DER standard" : should link to
http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (or
whatever is best reference for DER).

"this would simplify avoiding OpenSSL in consensus implementations"  -->
"this would make it easier for non-OpenSSL implementations"

"causing opcode failure"  : I know what you mean by "opcode failure", but
it might be good to be more explicit.

"since v0.8.0, and nearly no transactions" -->  "and very few
transactions..."

"reducing this avenue for malleability is useful on itself as well"  :
awkward English. How about just "This proposal has the added benefit of
reducing transaction malleability (see BIP62)."


-- 
--
Gavin Andresen

[-- Attachment #2: Type: text/html, Size: 1292 bytes --]

 permalink raw reply	[|nested]
2015-01-21 20:30   ` Pieter Wuille

  
From: Douglas Roark @ 2015-01-21 20:39 UTC (permalink / raw)
  To: Bitcoin Dev

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

On 2015/1/21 15:30, Pieter Wuille wrote:
> Thanks for the comments. I hope I have clarified the text a bit 
> accordingly.

You're welcome. All the revisions look good to me.

- ---
Douglas Roark
Senior Developer
Armory Technologies, Inc.
doug@bitcoinarmory•com
PGP key ID: 92ADC0D7
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
Comment: GPGTools - https://gpgtools.org

iQIcBAEBCgAGBQJUwA6WAAoJEGybVGGSrcDXvmEP/A09j4lq2P0RMqrvtwnDQRmH
oimbGwC2a/BbpACBegn0cdFYMURFFcec4gHKyvuN7xR4SRsgQ+Djq/KranAMkYbs
ZQVFGXRWdZhfsh7bY4zbBUj+H8c8PAsKL0D7S8r4iXviuUimXJXqESUYote9Ylz3
rwjiK3oRiCSMpTMiI3eDjrbQt5HHLw3hKL7W6zTerx64eCaO2JsIn/Pk4Krf9xwd
1ejpyqrK/9s90NPB0Qqieqbgg7WoQYP+ZMzFi5oNxtNrZjlOCNSQKLN0IXqnnMnS
+AoB4B5TUGCdLq3Wlo69mhLaLYNaPNHEoGNUwikXqsd5WeqsayuYDl36rI4MLWgB
ZBVO6D2BErqdqMTrmUEurubXMb6CCAuFu6iYjO3vucQ0l+7xD7OW/XiK7ZPNFuwj
2fJCjRHjqgDwKlIUF3Gh7BwRrT2iZRoFYWXDVRBMiJpHvs1+U79pQENp4BmQLWE+
xn3gX9r755mVDJL10MFM6jKijgTCGA2hEFjK2Vu1JJMeVSIGaOdEIen2DxS2mqnZ
b/t9VDxfbFQRw5pj2zHsvFDGBe7DEhvBSqbNtiPrY5/LITeP8Nt4CZ9PHrYPJV5A
ocUx98l1sqy7P0QiYzAEp5tpdjTS17MVNPt84JLJnk7wL+fDRfKKV3A7tI/ziFJe
hjW91YNTIrs+ZFLV/HJc
=Rjcd
-----END PGP SIGNATURE-----



 permalink raw reply	[|nested]
2015-01-21 20:37   ` Gavin Andresen

  2015-01-21 21:22     ` Pieter Wuille
  
From: Douglas Roark @ 2015-01-21 20:52 UTC (permalink / raw)
  To: bitcoin-development

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

On 2015/1/21 15:37, Gavin Andresen wrote:
> You mention the "DER standard" : should link to
> http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
>
> 
(or whatever is best reference for DER).

The link you gave is to the 2002 revision.
http://www.itu.int/rec/T-REC-X.690-200811-I/en has the latest revision
(Nov. 2008) and, AFAIK, is the most visible link to people searching
for X.690.

That said, X.690 is the definitive DER document (if not exactly the
easiest read). A link to it wouldn't hurt.

> "this would simplify avoiding OpenSSL in consensus implementations"
> --> "this would make it easier for non-OpenSSL implementations"
> 
> "causing opcode failure"  : I know what you mean by "opcode
> failure", but it might be good to be more explicit.
> 
> "since v0.8.0, and nearly no transactions" -->  "and very few 
> transactions..."
> 
> "reducing this avenue for malleability is useful on itself as well"
> : awkward English. How about just "This proposal has the added
> benefit of reducing transaction malleability (see BIP62)."

These all look good to me.

- ---
Douglas Roark
Senior Developer
Armory Technologies, Inc.
doug@bitcoinarmory•com
PGP key ID: 92ADC0D7
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
Comment: GPGTools - https://gpgtools.org

iQIcBAEBCgAGBQJUwBF8AAoJEGybVGGSrcDXBxcP/j9dKIeXkOvDFgSzON2hmjxT
nzpPcxovGt+ds1KqHMtuMm8+Mmc/Z8kOhKWzgQKYlxq8eQayQ4X/DUr97IY248NX
udVM6vEp/azPkXLOQnO6POpv8Il6twyuYGvFAHLiYe9k9qMfdSKZetx5xFKVBsuj
DhRY2TnWC7/OXNUrT7H5TPHDaGHyXeJ47XSOVjGQ/qxdczIzvmt11amZ/Vn2+uXh
Rvz+0CzbpXYaqYB04ZnIv5lxknmjWGbxPdht/SoOly8INehQacWnwUNZJpilKb6x
qEpbDGNxW2zHEFgfNHmtr9PCBN8KyiVnTt+VZpNNl7PJCxZiK6uiwyNxsmOBhBtm
Hrsvxb9GqEO/6PKesEo+Hi+6hhzzQRC6Xrf85SaFMzw9UjKuuRhstxx7XhudKFkN
lBJcxd40G7kWk0Gv+YQmhFUyXUBqloEFGrFlzWniFKaJGzZs5D0JPd83DsPI4RuT
0M63YabL8qplYN8vnyUXabFpzglvQdAFqZS2GsO6zwAeWrqxsojpcEpikj4T+izR
W1TzaRDdm5pEaMMxvb6wFIgO32uAjN1a8GrRj+uk5cxuiOuk/C4Ii18FYhqEtDNd
Gv80rPxWEOxbCoSqH6igPnySw3ePFLBzgC4eSLBTnqfKYltd8fTeS9wGy47+L1YO
qb5K/xlqt+REOdbTGLHi
=MNXG
-----END PGP SIGNATURE-----



 permalink raw reply	[|nested]
2015-01-21 20:37   ` Gavin Andresen
  2015-01-21 20:52     ` Douglas Roark

  
From: Pieter Wuille @ 2015-01-21 21:22 UTC (permalink / raw)
  To: Gavin Andresen;  Bitcoin Dev

On Wed, Jan 21, 2015 at 3:37 PM, Gavin Andresen <gavinandresen@gmail•com> wrote:
> DERSIG BIP looks great to me, just a few nit-picky changes suggested:
>
> You mention the "DER standard" : should link to
> http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (or
> whatever is best reference for DER).
>
> "this would simplify avoiding OpenSSL in consensus implementations"  -->
> "this would make it easier for non-OpenSSL implementations"
>
> "causing opcode failure"  : I know what you mean by "opcode failure", but it
> might be good to be more explicit.
>
> "since v0.8.0, and nearly no transactions" -->  "and very few
> transactions..."
>
> "reducing this avenue for malleability is useful on itself as well"  :
> awkward English. How about just "This proposal has the added benefit of
> reducing transaction malleability (see BIP62)."

Nit addressed, hopefully.

-- 
Pieter



 permalink raw reply	[|nested]
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
                   ` (2 preceding siblings ...)
  2015-01-21 19:29 ` Douglas Roark

  2015-01-21 22:57 ` 
                   ` 
  7 siblings, 0 replies; 32+ messages in thread
From: Andrew Poelstra @ 2015-01-21 20:27 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 2214 bytes --]


I've read this and it looks A-OK to me.

Andrew



On Tue, Jan 20, 2015 at 07:35:49PM -0500, Pieter Wuille wrote:
> Hello everyone,
> 
> We've been aware of the risk of depending on OpenSSL for consensus
> rules for a while, and were trying to get rid of this as part of BIP
> 62 (malleability protection), which was however postponed due to
> unforeseen complexities. The recent evens (see the thread titled
> "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> on this mailing list) have made it clear that the problem is very
> real, however, and I would prefer to have a fundamental solution for
> it sooner rather than later.
> 
> I therefore propose a softfork to make non-DER signatures illegal
> (they've been non-standard since v0.8.0). A draft BIP text can be
> found on:
> 
>     https://gist.github.com/sipa/5d12c343746dad376c80
> 
> The document includes motivation and specification. In addition, an
> implementation (including unit tests derived from the BIP text) can be
> found on:
> 
>     https://github.com/sipa/bitcoin/commit/bipstrictder
> 
> Comments/criticisms are very welcome, but I'd prefer keeping the
> discussion here on the mailinglist (which is more accessible than on
> the gist).
> 
> -- 
> Pieter
> 
> ------------------------------------------------------------------------------
> New Year. New Location. New Benefits. New Data Center in Ashburn, VA.
> GigeNET is offering a free month of service with a new server in Ashburn.
> Choose from 2 high performing configs, both with 100TB of bandwidth.
> Higher redundancy.Lower latency.Increased capacity.Completely compliant.
> http://p.sf.net/sfu/gigenet
> _______________________________________________
> Bitcoin-development mailing list
> Bitcoin-development@lists•sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bitcoin-development
> 

-- 
Andrew Poelstra
Mathematics Department, University of Texas at Austin
Email: apoelstra at wpsoftware.net
Web:   http://www.wpsoftware.net/andrew

"If they had taught a class on how to be the kind of citizen Dick Cheney
 worries about, I would have finished high school."   --Edward Snowden


[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]

^ permalink raw reply	[|nested] 32+ messages in thread
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
                   ` (3 preceding siblings ...)
  2015-01-21 20:27 ` Andrew Poelstra

  2015-01-22  0:32 ` 
                   ` 
  7 siblings, 0 replies; 32+ messages in thread
From: Dave Collins @ 2015-01-21 22:57 UTC (permalink / raw)
  To: Pieter Wuille, Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 1828 bytes --]

I'm really glad to see this proposal.  We already treat non-DER
signatures as non-standard in btcd and agree that extending them be
illegal as a part of a soft fork is a smart and sane thing to do.

It's also good to see the explicit use of signature parsing since it
matches what we already do as well because we noticed noticed OpenSSL's
notion of big numbers (unsigned) didn't agree with Go's (signed).  By
having the explicit signature scheme and checking clearly called out in
a BIP, it greatly lowers the chances of there being any disagreement
about what is valid or invalid due to an underlying dependency.

+1

On 1/20/2015 6:35 PM, Pieter Wuille wrote:
> Hello everyone,
> 
> We've been aware of the risk of depending on OpenSSL for consensus
> rules for a while, and were trying to get rid of this as part of BIP
> 62 (malleability protection), which was however postponed due to
> unforeseen complexities. The recent evens (see the thread titled
> "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> on this mailing list) have made it clear that the problem is very
> real, however, and I would prefer to have a fundamental solution for
> it sooner rather than later.
> 
> I therefore propose a softfork to make non-DER signatures illegal
> (they've been non-standard since v0.8.0). A draft BIP text can be
> found on:
> 
>     https://gist.github.com/sipa/5d12c343746dad376c80
> 
> The document includes motivation and specification. In addition, an
> implementation (including unit tests derived from the BIP text) can be
> found on:
> 
>     https://github.com/sipa/bitcoin/commit/bipstrictder
> 
> Comments/criticisms are very welcome, but I'd prefer keeping the
> discussion here on the mailinglist (which is more accessible than on
> the gist).
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 834 bytes --]

^ permalink raw reply	[|nested] 32+ messages in thread
* 
  2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
                   ` (4 preceding siblings ...)
  2015-01-21 22:57 ` Dave Collins

  2015-01-22  3:12   ` 
                     ` 
  2015-01-22 22:41 ` Zooko Wilcox-OHearn
  2015-01-26  5:14 ` Pieter Wuille
  7 siblings, 3 replies; 32+ messages in thread
From: Rusty Russell @ 2015-01-22  0:32 UTC (permalink / raw)
  To: Pieter Wuille, Bitcoin Dev

Pieter Wuille <pieter.wuille@gmail•com> writes:
> Hello everyone,
>
> We've been aware of the risk of depending on OpenSSL for consensus
> rules for a while, and were trying to get rid of this as part of BIP
> 62 (malleability protection), which was however postponed due to
> unforeseen complexities. The recent evens (see the thread titled
> "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> on this mailing list) have made it clear that the problem is very
> real, however, and I would prefer to have a fundamental solution for
> it sooner rather than later.

OK, I worked up a clearer (but more verbose) version with fewer
magic numbers.  More importantly, feel free to steal the test cases.

One weirdness is the restriction on maximum total length, rather than a
32 byte (33 with 0-prepad) limit on signatures themselves.

Apologies for my babytalk C++.  Am sure there's a neater way.

/* Licensed under Creative Commons zero (public domain). */
#include <vector>
#include <cstdlib>
#include <cassert>

#ifdef CLARIFY
bool ConsumeByte(const std::vector<unsigned char> &sig, size_t &off,
                 unsigned int &val)
{
    if (off >= sig.size()) return false;

    val = sig[off++];
    return true;
}

bool ConsumeTypeByte(const std::vector<unsigned char> &sig, size_t &off,
                     unsigned int t)
{
    unsigned int type;
    if (!ConsumeByte(sig, off, type)) return false;

    return (type == t);
}

bool ConsumeNonZeroLength(const std::vector<unsigned char> &sig, size_t &off,
                          unsigned int &len)
{
    if (!ConsumeByte(sig, off, len)) return false;

    // Zero-length integers are not allowed.
    return (len != 0);
}

bool ConsumeNumber(const std::vector<unsigned char> &sig, size_t &off,
                   unsigned int len)
{
    // Length of number should be within signature.
    if (off + len > sig.size()) return false;

    // Negative numbers are not allowed.
    if (sig[off] & 0x80) return false;

    // Zero bytes at the start are not allowed, unless it would
    // otherwise be interpreted as a negative number.
    if (len > 1 && (sig[off] == 0x00) && !(sig[off+1] & 0x80)) return false;

    // Consume number itself.
    off += len;
    return true;
}

// Consume a DER encoded integer, update off if successful.
bool ConsumeDERInteger(const std::vector<unsigned char> &sig, size_t &off) {
    unsigned int len;

    // Type byte must be "integer"
    if (!ConsumeTypeByte(sig, off, 0x02)) return false;
    if (!ConsumeNonZeroLength(sig, off, len)) return false;
    // Now the BE encoded value itself.
    if (!ConsumeNumber(sig, off, len)) return false;

    return true;
}

bool 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 cannot start with any
    //     null bytes, unless the first byte that follows is 0x80 or higher, in which
    //     case a single null byte is required.
    // * 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.

    // Accept empty signature as correctly encoded (but invalid) signature,
    // even though it is not strictly DER.
    if (sig.size() == 0) return true;

    // Maximum size constraint.
    if (sig.size() > 73) return false;

    size_t off = 0;

    // A signature is of type "compound".
    if (!ConsumeTypeByte(sig, off, 0x30)) return false;

    unsigned int len;
    if (!ConsumeNonZeroLength(sig, off, len)) return false;

    // Make sure the length covers the rest (except sighash).
    if (len + 1 != sig.size() - off) return false;

    // Check R value.
    if (!ConsumeDERInteger(sig, off)) return false;

    // Check S value.
    if (!ConsumeDERInteger(sig, off)) return false;

    // There should exactly one byte left (the sighash).
    return off + 1 == sig.size() ? true : false;
}
#else
bool 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)

    // Accept empty signature as correctly encoded (but invalid) signature,
    // even though it is not strictly DER. This avoids needing full DER signatures
    // in places where any invalid signature would do. Given that the empty string is
    // always invalid as signature, this is safe.
    if (sig.size() == 0) return true;

    // Minimum and maximum size constraints.
    if (sig.size() < 9) return false;
    if (sig.size() > 73) return false;

    // A signature is of type 0x30 (compound).
    if (sig[0] != 0x30) return false;

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

    // Extract the length of the R element.
    unsigned int lenR = sig[3];

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

    // Extract the length of the S element.
    unsigned int lenS = sig[5 + lenR];

    // 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;
 
    // Check whether the R element is an integer.
    if (sig[2] != 0x02) return false;

    // Zero-length integers are not allowed for R.
    if (lenR == 0) return false;

    // Negative numbers are not allowed for R.
    if (sig[4] & 0x80) return false;

    // 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;

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

    // Zero-length integers are not allowed for S.
    if (lenS == 0) return false;

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

    // 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;

    return true;
}
#endif

#define COMPOUND 0x30
#define NOT_COMPOUND 0x31

// Len gets adjusted by check() to be actual length with this offset.
#define LEN_OK 0
#define LEN_TOO_BIG 1
#define LEN_TOO_SMALL 0xff

#define INT 0x02
#define NOT_INT 0x03

#define MINIMAL_SIGLEN 1
#define MINIMAL_SIGVAL 0x0

#define NORMAL_SIGLEN 32
#define NORMAL_SIGVAL(S) S, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,              \
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,              \
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f

// 33 bytes is possible, with 0 prepended.
#define MAXIMAL_SIGLEN 33
#define MAXIMAL_SIGVAL(S) NORMAL_SIGVAL(S), 0x20

#define OVERSIZE_SIGLEN 34
#define OVERSIZE_SIGVAL(S) MAXIMAL_SIGVAL(S), 0x21

#define ZEROPAD_SIGLEN (1 + NORMAL_SIGLEN)
#define ZEROPAD_SIGVAL(S) 00, NORMAL_SIGVAL(S)

#define SIGHASH 0xf0

static bool check(const std::vector<unsigned char> &sig)
{
    std::vector<unsigned char> fixed = sig;

    // Fixup length
    if (fixed.size() > 1)
        fixed[1] += fixed.size() - 3;
    return IsValidSignatureEncoding(fixed);
}

#define good(arr) assert(check(std::vector<unsigned char>(arr, arr+sizeof(arr))))
#define bad(arr) assert(!check(std::vector<unsigned char>(arr, arr+sizeof(arr))))

// The OK cases.
static unsigned char zerolen[] = { };
static unsigned char normal[] = { COMPOUND, LEN_OK,
                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                  SIGHASH };
static unsigned char min_r[] = { COMPOUND, LEN_OK,
                                 INT, MINIMAL_SIGLEN, MINIMAL_SIGVAL,
                                 INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                 SIGHASH };
static unsigned char min_s[] = { COMPOUND, LEN_OK,
                                 INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                 INT, MINIMAL_SIGLEN, MINIMAL_SIGVAL,
                                 SIGHASH };
static unsigned char max_r[] = { COMPOUND, LEN_OK,
                                 INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x1),
                                 INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                 SIGHASH };
static unsigned char max_s[] = { COMPOUND, LEN_OK,
                                 INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                 INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x2),
                                 SIGHASH };
// As long as total size doesn't go over, a single sig is allowed > 33 bytes
static unsigned char wierd_s_len[] = { COMPOUND, LEN_OK,
                                       INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x1),
                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                       SIGHASH };
static unsigned char wierd_r_len[] = { COMPOUND, LEN_OK,
                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                       INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x2),
                                       SIGHASH };
static unsigned char zeropad_s[] = { COMPOUND, LEN_OK,
                                     INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x81),
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                     SIGHASH };
static unsigned char zeropad_r[] = { COMPOUND, LEN_OK,
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                     INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x82),
                                     SIGHASH };


// The fail cases.
static unsigned char not_compound[] = { NOT_COMPOUND, LEN_OK,
                                        INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                        INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                        SIGHASH };
static unsigned char short_len[] = { COMPOUND, LEN_TOO_SMALL,
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                     SIGHASH };
static unsigned char long_len[] = { COMPOUND, LEN_TOO_BIG,
                                    INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                    INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                    SIGHASH };
static unsigned char r_notint[] = { COMPOUND, LEN_OK,
                                    NOT_INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                    INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                    SIGHASH };
static unsigned char s_notint[] = { COMPOUND, LEN_OK,
                                    INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                    NOT_INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                    SIGHASH };
static unsigned char s_oversig[] = { COMPOUND, LEN_OK,
                                     INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x1),
                                     INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x2),
                                     SIGHASH };
static unsigned char r_oversig[] = { COMPOUND, LEN_OK,
                                     INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x1),
                                     INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x2),
                                     SIGHASH };
static unsigned char s_negative[] = { COMPOUND, LEN_OK,
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x81),
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                      SIGHASH };
static unsigned char r_negative[] = { COMPOUND, LEN_OK,
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x82),
                                      SIGHASH };
static unsigned char zeropad_bad_s[] = { COMPOUND, LEN_OK,
                                         INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x1),
                                         INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                         SIGHASH };
static unsigned char zeropad_bad_r[] = { COMPOUND, LEN_OK,
                                         INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                         INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x2),
                                         SIGHASH };
static unsigned char missing_sighash[] = { COMPOUND, LEN_OK,
                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2) };
static unsigned char extra_byte[] = { COMPOUND, LEN_OK,
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                      SIGHASH, 0 };

// Bad signature lengths
static unsigned char zerolen_r[] = { COMPOUND, LEN_OK,
                                     INT, 0,
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                     SIGHASH };
static unsigned char zerolen_s[] = { COMPOUND, LEN_OK,
                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                     INT, 0,
                                     SIGHASH };
static unsigned char overlen_r_by_1[] = { COMPOUND, LEN_OK,
                                          INT, NORMAL_SIGLEN + 1 + 1 + NORMAL_SIGLEN + 1 + 1, NORMAL_SIGVAL(0x1),
                                          INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                          SIGHASH };
static unsigned char overlen_s_by_1[] = { COMPOUND, LEN_OK,
                                          INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                          INT, NORMAL_SIGLEN+1+1, NORMAL_SIGVAL(0x2),
                                          SIGHASH };
static unsigned char underlen_r_by_1[] = { COMPOUND, LEN_OK,
                                           INT, NORMAL_SIGLEN-1, NORMAL_SIGVAL(0x1),
                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
                                           SIGHASH };
static unsigned char underlen_s_by_1[] = { COMPOUND, LEN_OK,
                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
                                           INT, NORMAL_SIGLEN-1, NORMAL_SIGVAL(0x2),
                                           SIGHASH };

int main()
{
    good(zerolen);
    good(normal);
    good(min_r);
    good(min_s);
    good(max_r);
    good(max_s);
    good(wierd_s_len);
    good(wierd_r_len);
    good(zeropad_s);
    good(zeropad_r);

    // Try different amounts of truncation.
    for (size_t i = 1; i < sizeof(normal)-1; i++)
        assert(!check(std::vector<unsigned char>(normal, normal+i)));

    bad(not_compound);
    bad(short_len);
    bad(long_len);
    bad(r_notint);
    bad(s_notint);
    bad(s_oversig);
    bad(r_oversig);
    bad(s_negative);
    bad(r_negative);
    bad(s_negative);
    bad(r_negative);
    bad(zeropad_bad_s);
    bad(zeropad_bad_r);
    bad(zerolen_r);
    bad(zerolen_s);
    bad(overlen_r_by_1);
    bad(overlen_s_by_1);
    bad(underlen_r_by_1);
    bad(underlen_s_by_1);
    bad(missing_sighash);
    bad(extra_byte);

    return 0;
}





^ permalink raw reply	[|nested] 32+ messages in thread
2015-01-22  0:32 ` Rusty Russell
  2015-01-22  3:12   ` David Vorick

  2015-01-22  4:20     ` Pieter Wuille
  2015-01-25 14:34   ` Pieter Wuille
  
From: Matt Whitlock @ 2015-01-22  4:18 UTC (permalink / raw)
  To: Rusty Russell;  bitcoin-development

To be more in the C++ spirit, I would suggest changing the (const std::vector<unsigned char> &sig, size_t &off) parameters to (std::vector<unsigned char>::const_iterator &itr, std::vector<unsigned char>::const_iterator end).

Example:

bool ConsumeNumber(std::vector<unsigned char>::const_iterator &itr, std::vector<unsigned char>::const_iterator end, unsigned int len)
{
	// Length of number should be within signature.
	if (itr + len >= end) return false;
 
	// Negative numbers are not allowed.
	if (*itr & 0x80) return false;
 
	// Zero bytes at the start are not allowed, unless it would
	// otherwise be interpreted as a negative number.
	if (len > 1 && (*itr == 0x00) && !(*(itr + 1) & 0x80)) return false;
 
	// Consume number itself.
	itr += len;
	return true;
}


On Thursday, 22 January 2015, at 11:02 am, Rusty Russell wrote:
> Pieter Wuille <pieter.wuille@gmail•com> writes:
> > Hello everyone,
> >
> > We've been aware of the risk of depending on OpenSSL for consensus
> > rules for a while, and were trying to get rid of this as part of BIP
> > 62 (malleability protection), which was however postponed due to
> > unforeseen complexities. The recent evens (see the thread titled
> > "OpenSSL 1.0.0p / 1.0.1k incompatible, causes blockchain rejection."
> > on this mailing list) have made it clear that the problem is very
> > real, however, and I would prefer to have a fundamental solution for
> > it sooner rather than later.
> 
> OK, I worked up a clearer (but more verbose) version with fewer
> magic numbers.  More importantly, feel free to steal the test cases.
> 
> One weirdness is the restriction on maximum total length, rather than a
> 32 byte (33 with 0-prepad) limit on signatures themselves.
> 
> Apologies for my babytalk C++.  Am sure there's a neater way.
> 
> /* Licensed under Creative Commons zero (public domain). */
> #include <vector>
> #include <cstdlib>
> #include <cassert>
> 
> #ifdef CLARIFY
> bool ConsumeByte(const std::vector<unsigned char> &sig, size_t &off,
>                  unsigned int &val)
> {
>     if (off >= sig.size()) return false;
> 
>     val = sig[off++];
>     return true;
> }
> 
> bool ConsumeTypeByte(const std::vector<unsigned char> &sig, size_t &off,
>                      unsigned int t)
> {
>     unsigned int type;
>     if (!ConsumeByte(sig, off, type)) return false;
> 
>     return (type == t);
> }
> 
> bool ConsumeNonZeroLength(const std::vector<unsigned char> &sig, size_t &off,
>                           unsigned int &len)
> {
>     if (!ConsumeByte(sig, off, len)) return false;
> 
>     // Zero-length integers are not allowed.
>     return (len != 0);
> }
> 
> bool ConsumeNumber(const std::vector<unsigned char> &sig, size_t &off,
>                    unsigned int len)
> {
>     // Length of number should be within signature.
>     if (off + len > sig.size()) return false;
> 
>     // Negative numbers are not allowed.
>     if (sig[off] & 0x80) return false;
> 
>     // Zero bytes at the start are not allowed, unless it would
>     // otherwise be interpreted as a negative number.
>     if (len > 1 && (sig[off] == 0x00) && !(sig[off+1] & 0x80)) return false;
> 
>     // Consume number itself.
>     off += len;
>     return true;
> }
> 
> // Consume a DER encoded integer, update off if successful.
> bool ConsumeDERInteger(const std::vector<unsigned char> &sig, size_t &off) {
>     unsigned int len;
> 
>     // Type byte must be "integer"
>     if (!ConsumeTypeByte(sig, off, 0x02)) return false;
>     if (!ConsumeNonZeroLength(sig, off, len)) return false;
>     // Now the BE encoded value itself.
>     if (!ConsumeNumber(sig, off, len)) return false;
> 
>     return true;
> }
> 
> bool 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 cannot start with any
>     //     null bytes, unless the first byte that follows is 0x80 or higher, in which
>     //     case a single null byte is required.
>     // * 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.
> 
>     // Accept empty signature as correctly encoded (but invalid) signature,
>     // even though it is not strictly DER.
>     if (sig.size() == 0) return true;
> 
>     // Maximum size constraint.
>     if (sig.size() > 73) return false;
> 
>     size_t off = 0;
> 
>     // A signature is of type "compound".
>     if (!ConsumeTypeByte(sig, off, 0x30)) return false;
> 
>     unsigned int len;
>     if (!ConsumeNonZeroLength(sig, off, len)) return false;
> 
>     // Make sure the length covers the rest (except sighash).
>     if (len + 1 != sig.size() - off) return false;
> 
>     // Check R value.
>     if (!ConsumeDERInteger(sig, off)) return false;
> 
>     // Check S value.
>     if (!ConsumeDERInteger(sig, off)) return false;
> 
>     // There should exactly one byte left (the sighash).
>     return off + 1 == sig.size() ? true : false;
> }
> #else
> bool 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)
> 
>     // Accept empty signature as correctly encoded (but invalid) signature,
>     // even though it is not strictly DER. This avoids needing full DER signatures
>     // in places where any invalid signature would do. Given that the empty string is
>     // always invalid as signature, this is safe.
>     if (sig.size() == 0) return true;
> 
>     // Minimum and maximum size constraints.
>     if (sig.size() < 9) return false;
>     if (sig.size() > 73) return false;
> 
>     // A signature is of type 0x30 (compound).
>     if (sig[0] != 0x30) return false;
> 
>     // Make sure the length covers the entire signature.
>     if (sig[1] != sig.size() - 3) return false;
> 
>     // Extract the length of the R element.
>     unsigned int lenR = sig[3];
> 
>     // Make sure the length of the S element is still inside the signature.
>     if (5 + lenR >= sig.size()) return false;
> 
>     // Extract the length of the S element.
>     unsigned int lenS = sig[5 + lenR];
> 
>     // 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;
>  
>     // Check whether the R element is an integer.
>     if (sig[2] != 0x02) return false;
> 
>     // Zero-length integers are not allowed for R.
>     if (lenR == 0) return false;
> 
>     // Negative numbers are not allowed for R.
>     if (sig[4] & 0x80) return false;
> 
>     // 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;
> 
>     // Check whether the S element is an integer.
>     if (sig[lenR + 4] != 0x02) return false;
> 
>     // Zero-length integers are not allowed for S.
>     if (lenS == 0) return false;
> 
>     // Negative numbers are not allowed for S.
>     if (sig[lenR + 6] & 0x80) return false;
> 
>     // 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;
> 
>     return true;
> }
> #endif
> 
> #define COMPOUND 0x30
> #define NOT_COMPOUND 0x31
> 
> // Len gets adjusted by check() to be actual length with this offset.
> #define LEN_OK 0
> #define LEN_TOO_BIG 1
> #define LEN_TOO_SMALL 0xff
> 
> #define INT 0x02
> #define NOT_INT 0x03
> 
> #define MINIMAL_SIGLEN 1
> #define MINIMAL_SIGVAL 0x0
> 
> #define NORMAL_SIGLEN 32
> #define NORMAL_SIGVAL(S) S, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \
>         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,              \
>         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,              \
>         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
> 
> // 33 bytes is possible, with 0 prepended.
> #define MAXIMAL_SIGLEN 33
> #define MAXIMAL_SIGVAL(S) NORMAL_SIGVAL(S), 0x20
> 
> #define OVERSIZE_SIGLEN 34
> #define OVERSIZE_SIGVAL(S) MAXIMAL_SIGVAL(S), 0x21
> 
> #define ZEROPAD_SIGLEN (1 + NORMAL_SIGLEN)
> #define ZEROPAD_SIGVAL(S) 00, NORMAL_SIGVAL(S)
> 
> #define SIGHASH 0xf0
> 
> static bool check(const std::vector<unsigned char> &sig)
> {
>     std::vector<unsigned char> fixed = sig;
> 
>     // Fixup length
>     if (fixed.size() > 1)
>         fixed[1] += fixed.size() - 3;
>     return IsValidSignatureEncoding(fixed);
> }
> 
> #define good(arr) assert(check(std::vector<unsigned char>(arr, arr+sizeof(arr))))
> #define bad(arr) assert(!check(std::vector<unsigned char>(arr, arr+sizeof(arr))))
> 
> // The OK cases.
> static unsigned char zerolen[] = { };
> static unsigned char normal[] = { COMPOUND, LEN_OK,
>                                   INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                   INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                   SIGHASH };
> static unsigned char min_r[] = { COMPOUND, LEN_OK,
>                                  INT, MINIMAL_SIGLEN, MINIMAL_SIGVAL,
>                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                  SIGHASH };
> static unsigned char min_s[] = { COMPOUND, LEN_OK,
>                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                  INT, MINIMAL_SIGLEN, MINIMAL_SIGVAL,
>                                  SIGHASH };
> static unsigned char max_r[] = { COMPOUND, LEN_OK,
>                                  INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x1),
>                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                  SIGHASH };
> static unsigned char max_s[] = { COMPOUND, LEN_OK,
>                                  INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                  INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x2),
>                                  SIGHASH };
> // As long as total size doesn't go over, a single sig is allowed > 33 bytes
> static unsigned char wierd_s_len[] = { COMPOUND, LEN_OK,
>                                        INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x1),
>                                        INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                        SIGHASH };
> static unsigned char wierd_r_len[] = { COMPOUND, LEN_OK,
>                                        INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                        INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x2),
>                                        SIGHASH };
> static unsigned char zeropad_s[] = { COMPOUND, LEN_OK,
>                                      INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x81),
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                      SIGHASH };
> static unsigned char zeropad_r[] = { COMPOUND, LEN_OK,
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                      INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x82),
>                                      SIGHASH };
> 
> 
> // The fail cases.
> static unsigned char not_compound[] = { NOT_COMPOUND, LEN_OK,
>                                         INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                         INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                         SIGHASH };
> static unsigned char short_len[] = { COMPOUND, LEN_TOO_SMALL,
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                      SIGHASH };
> static unsigned char long_len[] = { COMPOUND, LEN_TOO_BIG,
>                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                     SIGHASH };
> static unsigned char r_notint[] = { COMPOUND, LEN_OK,
>                                     NOT_INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                     SIGHASH };
> static unsigned char s_notint[] = { COMPOUND, LEN_OK,
>                                     INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                     NOT_INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                     SIGHASH };
> static unsigned char s_oversig[] = { COMPOUND, LEN_OK,
>                                      INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x1),
>                                      INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x2),
>                                      SIGHASH };
> static unsigned char r_oversig[] = { COMPOUND, LEN_OK,
>                                      INT, MAXIMAL_SIGLEN, MAXIMAL_SIGVAL(0x1),
>                                      INT, OVERSIZE_SIGLEN, OVERSIZE_SIGVAL(0x2),
>                                      SIGHASH };
> static unsigned char s_negative[] = { COMPOUND, LEN_OK,
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x81),
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                       SIGHASH };
> static unsigned char r_negative[] = { COMPOUND, LEN_OK,
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x82),
>                                       SIGHASH };
> static unsigned char zeropad_bad_s[] = { COMPOUND, LEN_OK,
>                                          INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x1),
>                                          INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                          SIGHASH };
> static unsigned char zeropad_bad_r[] = { COMPOUND, LEN_OK,
>                                          INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                          INT, ZEROPAD_SIGLEN, ZEROPAD_SIGVAL(0x2),
>                                          SIGHASH };
> static unsigned char missing_sighash[] = { COMPOUND, LEN_OK,
>                                            INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                            INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2) };
> static unsigned char extra_byte[] = { COMPOUND, LEN_OK,
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                       INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                       SIGHASH, 0 };
> 
> // Bad signature lengths
> static unsigned char zerolen_r[] = { COMPOUND, LEN_OK,
>                                      INT, 0,
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                      SIGHASH };
> static unsigned char zerolen_s[] = { COMPOUND, LEN_OK,
>                                      INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                      INT, 0,
>                                      SIGHASH };
> static unsigned char overlen_r_by_1[] = { COMPOUND, LEN_OK,
>                                           INT, NORMAL_SIGLEN + 1 + 1 + NORMAL_SIGLEN + 1 + 1, NORMAL_SIGVAL(0x1),
>                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                           SIGHASH };
> static unsigned char overlen_s_by_1[] = { COMPOUND, LEN_OK,
>                                           INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                           INT, NORMAL_SIGLEN+1+1, NORMAL_SIGVAL(0x2),
>                                           SIGHASH };
> static unsigned char underlen_r_by_1[] = { COMPOUND, LEN_OK,
>                                            INT, NORMAL_SIGLEN-1, NORMAL_SIGVAL(0x1),
>                                            INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x2),
>                                            SIGHASH };
> static unsigned char underlen_s_by_1[] = { COMPOUND, LEN_OK,
>                                            INT, NORMAL_SIGLEN, NORMAL_SIGVAL(0x1),
>                                            INT, NORMAL_SIGLEN-1, NORMAL_SIGVAL(0x2),
>                                            SIGHASH };
> 
> int main()
> {
>     good(zerolen);
>     good(normal);
>     good(min_r);
>     good(min_s);
>     good(max_r);
>     good(max_s);
>     good(wierd_s_len);
>     good(wierd_r_len);
>     good(zeropad_s);
>     good(zeropad_r);
> 
>     // Try different amounts of truncation.
>     for (size_t i = 1; i < sizeof(normal)-1; i++)
>         assert(!check(std::vector<unsigned char>(normal, normal+i)));
> 
>     bad(not_compound);
>     bad(short_len);
>     bad(long_len);
>     bad(r_notint);
>     bad(s_notint);
>     bad(s_oversig);
>     bad(r_oversig);
>     bad(s_negative);
>     bad(r_negative);
>     bad(s_negative);
>     bad(r_negative);
>     bad(zeropad_bad_s);
>     bad(zeropad_bad_r);
>     bad(zerolen_r);
>     bad(zerolen_s);
>     bad(overlen_r_by_1);
>     bad(overlen_s_by_1);
>     bad(underlen_r_by_1);
>     bad(underlen_s_by_1);
>     bad(missing_sighash);
>     bad(extra_byte);
> 
>     return 0;
> }
> 
> 
> 
> ------------------------------------------------------------------------------
> New Year. New Location. New Benefits. New Data Center in Ashburn, VA.
> GigeNET is offering a free month of service with a new server in Ashburn.
> Choose from 2 high performing configs, both with 100TB of bandwidth.
> Higher redundancy.Lower latency.Increased capacity.Completely compliant.
> http://p.sf.net/sfu/gigenet
> _______________________________________________
> Bitcoin-development mailing list
> Bitcoin-development@lists•sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bitcoin-development



 permalink raw reply	[|nested]
2015-01-22  4:18   ` Matt Whitlock

  
From: Pieter Wuille @ 2015-01-22  4:20 UTC (permalink / raw)
  To: Matt Whitlock;  Bitcoin Dev

On Wed, Jan 21, 2015 at 11:18 PM, Matt Whitlock <bip@mattwhitlock•name> wrote:
> To be more in the C++ spirit, I would suggest changing the (const std::vector<unsigned char> &sig, size_t &off) parameters to (std::vector<unsigned char>::const_iterator &itr, std::vector<unsigned char>::const_iterator end).

I agree that is more in the spirit of C++, but part of the motivation
for including C++ code that it mostly matches the exact code that has
been used in the past two major Bitcoin Core releases (to interpret
signatures as standard).

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
                   ` (5 preceding siblings ...)
  2015-01-22  0:32 ` Rusty Russell

  2015-01-25 16:57   ` Pieter Wuille
  2015-01-26  5:14 ` Pieter Wuille
  
From: Zooko Wilcox-OHearn @ 2015-01-22 22:41 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

.Hi there. Thank you for your work on this.

I've looked over https://gist.github.com/sipa/5d12c343746dad376c80 and
https://github.com/sipa/bitcoin/commit/bipstrictder . I didn't
actually audit the included reference implementation of
IsValidSignatureEncoding(), and I didn't check whether the test
vectors in https://github.com/sipa/bitcoin/commit/f94e806f8bfa007a3de4b45fa3c9860f2747e427
exercise all of the branches that are changed by this patch.

I have the following comments:

* It seems like a good idea to do this.

* I don't see any problem with using the upgrade mechanism from BIP 34
for this. It's cool! I'm happy that such a mechanism seems to work in
practice.

* Should the bipstrictder give a rationale or link to why accept the
0-length sig as correctly-encoded-but-invalid? I guess the rationale
is an efficiency issue as described in the log entry for
https://github.com/sipa/bitcoin/commit/041f1e3597812c250ebedbd8f4ef1565591d2c34
.

* Does this mean there are still multiple ways to encode a correctly
encoded but invalid signature, one of which is the 0-length string?
Would it make sense for this change to also treat any *other*
correctly-encoded-but-invalid sig (besides the 0-length string) as
incorrectly-encoded? Did I just step in some BIP62?

* It would be good to verify that all the branches of the new
IsDERSignature() from
https://github.com/sipa/bitcoin/commit/0c427135151a6bed657438ffb2e670be84eb3642
are tested by the test vectors in
https://github.com/sipa/bitcoin/commit/f94e806f8bfa007a3de4b45fa3c9860f2747e427
. Eyeballing it, there are about 20 branches touched by the patch, and
about 24 new test vectors.

* It would be good to finish the TODOs in
https://github.com/sipa/bitcoin/commit/b7986119a5d41337fea1e83804ed6223438158ec
so that it was actually testing the upgrade behavior.

* missing comment:
https://github.com/sipa/bitcoin/commit/e186f6a80161f9fa45fbced82ab1d22f081b942c#commitcomment-9406643

Okay, that's all I've got. Hope it helps! Thanks again for your good work!

Regards,

Zooko



 permalink raw reply	[|nested]
2015-01-22  0:32 ` Rusty Russell
  2015-01-22  3:12   ` David Vorick
  2015-01-22  4:18   ` Matt Whitlock

  2015-01-25 14:48     ` Gregory Maxwell
  
From: Pieter Wuille @ 2015-01-25 14:34 UTC (permalink / raw)
  To: Rusty Russell;  Bitcoin Dev

On Wed, Jan 21, 2015 at 8:32 PM, Rusty Russell <rusty@rustcorp•com.au> wrote:
> One weirdness is the restriction on maximum total length, rather than a
> 32 byte (33 with 0-prepad) limit on signatures themselves.

Glad that you point this out; I believe that's a weakness with more
impact now that this function is used for consensus. Let me clarify.

This function was originally written for Bitcoin Core v0.8.0, where it
was only used to enforce non-standardness, not consensus. In that
setting, there was no need to require a maximum length for the R and S
arguments, as overly-long R or S values (which, because of a further
rule, do not have excessive padding) will always result in integers >=
2^256, which means the encoded signature would never be valid
according to the ECDSA specification. A restriction on the total
length is required however, as BER allows multi-byte length
descriptors, which this function cannot (and shouldn't, as it's not
DER) parse.

However, in the currently proposed soft fork, non-DER results in
immediate script failure, which is distinguishable from invalid
signatures (by negating the result of a CHECKSIG, for example using a
NOT after it). I must admit that having invalid signatures with
overly-long R or S but acceptable R+S size be distinguishable from
invalid signatures where R+S is too large is ugly, and unnecessary.

Adding individual R and S length restrictions (ideally: saying that no
more than 32 bytes, excluding the padding 0 byte in front, is invalid)
would be trivial, but it means deviating slightly from the
standardness rule implementation that has been deployed for a while.
There should not really be much risk in doing so, as there are still
no node implementation releases (apart from the v0.10.0 rc's) that
would mine a CHECKSIG whose result is negated.

So, I think there are two options:
* Just add this R/S length restriction rule as a standardness
requirement, but not make it part of the soft fork. A later softfork
can then add this easily. The same can be done for several other
changes if they are deemed useful, like only allowing 0 (the empty
array) as invalid signature (any other causes failure script
immediately), requiring correct encoding even for non-evaluated
signatures, ...
* Add it to the softfork now, and be done with it.

Opinions?

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-25 14:34   ` Pieter Wuille

  2015-02-03  0:44       ` Pieter Wuille
  
From: Gregory Maxwell @ 2015-01-25 14:48 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

On Sun, Jan 25, 2015 at 2:34 PM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
> * Add it to the softfork now, and be done with it.

Initially I was of the opinion that we couldn't do that, because
soft-forks which hit transactions many nodes would relay+mine creates
a forking risk... but with the realization that imbalanced R/S plus
checksig-not would only be work with 0.10rc/git changed my mind.
Unlike two years ago miners no longer appear to be racing the bleeding
edge, and it's never show up in a release. Obviously the next RC would
also make those non-standard. And then we'll have some non-trivial
amount of time before the soft-fork activates for whatever stragglers
there are on 0.10 prerelease code to update. The deployment of the
soft-fork rules themselves will already drive people to update.

In terms of being robust to implementation differences, not permitting
overlarge R/S is obviously prudent.

So I think we should just go ahead with R/S length upper bounds as
both IsStandard and in STRICTDER.



 permalink raw reply	[|nested]
2015-01-22 22:41 ` Zooko Wilcox-OHearn

  
From: Pieter Wuille @ 2015-01-25 16:57 UTC (permalink / raw)
  To: Zooko Wilcox-OHearn;  Bitcoin Dev

On Thu, Jan 22, 2015 at 6:41 PM, Zooko Wilcox-OHearn
<zooko@leastauthority•com> wrote:
> * Should the bipstrictder give a rationale or link to why accept the
> 0-length sig as correctly-encoded-but-invalid? I guess the rationale
> is an efficiency issue as described in the log entry for
> https://github.com/sipa/bitcoin/commit/041f1e3597812c250ebedbd8f4ef1565591d2c34

I've lately been updating the BIP text without updating the code in
the repository; I've synced them now. The sigsize=0 case was actually
already handled elsewhere already, so I removed the code and added a
comment about it now in the BIP text.

> * Does this mean there are still multiple ways to encode a correctly
> encoded but invalid signature, one of which is the 0-length string?
> Would it make sense for this change to also treat any *other*
> correctly-encoded-but-invalid sig (besides the 0-length string) as
> incorrectly-encoded? Did I just step in some BIP62?

You didn't miss anything; that's correct. In fact, Peter Todd already
pointed out the possibility of making non-empty invalid signatures
illegal. The reason for not doing it yet is that I'd like this BIP to
be minimal and uncontroversial - it's a real problem we want to fix as
fast as is reasonable. It wouldn't be hard to make this a standardness
rule though, and perhaps later softfork it in as consensus rule if
there was sufficient agreement about it.

> * It would be good to verify that all the branches of the new
> IsDERSignature() from
> https://github.com/sipa/bitcoin/commit/0c427135151a6bed657438ffb2e670be84eb3642
> are tested by the test vectors in
> https://github.com/sipa/bitcoin/commit/f94e806f8bfa007a3de4b45fa3c9860f2747e427
> . Eyeballing it, there are about 20 branches touched by the patch, and
> about 24 new test vectors.

A significiant part of DERSIG behaviour (which didn't change, only the
cases in which it is enforced) was already tested, in fact. Some
branches remained untested however; I've added extra test cases in the
repository. They give 100% coverage for IsValidSignatureEncoding (the
new name for IsDERSignature) now (tested with gcov).

> * It would be good to finish the TODOs in
> https://github.com/sipa/bitcoin/commit/b7986119a5d41337fea1e83804ed6223438158ec
> so that it was actually testing the upgrade behavior.

I agree, but that requires very significant changes to the codebase,
as we currently have no way to mine blocks with non-acceptable
transactions. Ideally, the RPC tests gain some means of
building/mining blocks from without the Python test framework. Things
like that would make the code changes also hard to backport, which we
definitely will need to do to roll this out quickly.

> * missing comment:
> https://github.com/sipa/bitcoin/commit/e186f6a80161f9fa45fbced82ab1d22f081b942c#commitcomment-9406643

Fixed.

> Okay, that's all I've got. Hope it helps! Thanks again for your good work!

Thanks!

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-21  0:35 [Bitcoin-development] [softfork proposal] Strict DER signatures Pieter Wuille
                   ` (6 preceding siblings ...)
  2015-01-22 22:41 ` Zooko Wilcox-OHearn

  2015-01-26 18:35   ` Gregory Maxwell
  
From: Pieter Wuille @ 2015-01-26  5:14 UTC (permalink / raw)
  To: Bitcoin Dev, Gregory Maxwell

On Tue, Jan 20, 2015 at 8:35 PM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
> I therefore propose a softfork to make non-DER signatures illegal
> (they've been non-standard since v0.8.0). A draft BIP text can be
> found on:
>
>     https://gist.github.com/sipa/5d12c343746dad376c80

I'd like to request a BIP number for this.

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-26  5:14 ` Pieter Wuille

  2015-01-28  6:24     ` Wladimir
  2015-02-06 21:38     ` Pieter Wuille
  
From: Gregory Maxwell @ 2015-01-26 18:35 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

On Mon, Jan 26, 2015 at 5:14 AM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
> On Tue, Jan 20, 2015 at 8:35 PM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
>> I therefore propose a softfork to make non-DER signatures illegal
>> (they've been non-standard since v0.8.0). A draft BIP text can be
>> found on:
>>
>>     https://gist.github.com/sipa/5d12c343746dad376c80
>
> I'd like to request a BIP number for this.

Sure. BIP0066. There was also some feedback on Bitcointalk, which I
think you've addressed:
https://bitcointalk.org/index.php?topic=932054.0 I also had off-list
positive feedback from Amir Taak, so we have positive feedback from
several implementers.

One of the points that was raised which we'd discussed pre-proposal
that was brought up there that I thought I should summarize here was
the possibility that someone had previously authored an nlocked spend
with an invalidly encoded signature. In those cases the signature can
just be mutated to get it mined, and would need to be already to pass
IsStandard rules. A case that isn't covered if if they have a chain of
transactions after that nlocked transaction, but those cases would
already be at extreme risk of malleability (esp since their unchanged
form is non-standard), and that coupled with the fact that avoiding
this would undermine the intent of the BIP (independence from  a
specific encoding scheme) seems to have been convincing as much.



 permalink raw reply	[|nested]
2015-01-26 18:35   ` Gregory Maxwell

  2015-02-06 21:38     ` Pieter Wuille
  
From: Wladimir @ 2015-01-28  6:24 UTC (permalink / raw)
  To: Bitcoin Dev


On Mon, 26 Jan 2015, Gregory Maxwell wrote:

> On Mon, Jan 26, 2015 at 5:14 AM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
>> On Tue, Jan 20, 2015 at 8:35 PM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
>>> I therefore propose a softfork to make non-DER signatures illegal
>>> (they've been non-standard since v0.8.0). A draft BIP text can be
>>> found on:
>>>
>>>     https://gist.github.com/sipa/5d12c343746dad376c80
>>
>> I'd like to request a BIP number for this.
>
> Sure. BIP0066. There was also some feedback on Bitcointalk, which I
> think you've addressed

Progress information for the list: there is now a pull request
implementing the strict DER verification behavior, as well as the
deployment specified in BIP66 for Bitcoin Core. It needs
your review and testing:

https://github.com/bitcoin/bitcoin/pull/5713

Wladimir



 permalink raw reply	[|nested]
2015-01-25 14:48     ` Gregory Maxwell

  2015-02-03  2:21         ` Gregory Maxwell
  2015-02-03 12:00         ` Wladimir
  
From: Pieter Wuille @ 2015-02-03  0:44 UTC (permalink / raw)
  To: Gregory Maxwell;  Bitcoin Dev

On Sun, Jan 25, 2015 at 6:48 AM, Gregory Maxwell <gmaxwell@gmail•com> wrote:
> So I think we should just go ahead with R/S length upper bounds as
> both IsStandard and in STRICTDER.

I would like to fix this at some point in any case.

If we want to do that, we must at least have signatures with too-long
R or S values as non-standard.

One way to do that is to just - right now - add a patch to 0.10 to
make those non-standard. This requires another validation flag, with a
bunch of switching logic.

The much simpler alternative is just adding this to BIP66's DERSIG
right now, which is a one-line change that's obviously softforking. Is
anyone opposed to doing so at this stage?

-- 
Pieter



 permalink raw reply	[|nested]
2015-02-03  0:44       ` Pieter Wuille

  2015-02-03 12:00         ` Wladimir
  
From: Gregory Maxwell @ 2015-02-03  2:21 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

On Tue, Feb 3, 2015 at 12:44 AM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
> The much simpler alternative is just adding this to BIP66's DERSIG
> right now, which is a one-line change that's obviously softforking. Is
> anyone opposed to doing so at this stage?

Thats my preference.



 permalink raw reply	[|nested]
2015-02-03  0:44       ` Pieter Wuille
  2015-02-03  2:21         ` Gregory Maxwell

  2015-02-03 14:30           ` Alex Morcos
  2015-02-03 18:15           ` Pieter Wuille
  
From: Wladimir @ 2015-02-03 12:00 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

> One way to do that is to just - right now - add a patch to 0.10 to
> make those non-standard. This requires another validation flag, with a
> bunch of switching logic.
>
> The much simpler alternative is just adding this to BIP66's DERSIG
> right now, which is a one-line change that's obviously softforking. Is
> anyone opposed to doing so at this stage?

Not opposed, but is kind of late for 0.10, I had hoped to tag rc4 today.

Wladimir



 permalink raw reply	[|nested]
2015-02-03 12:00         ` Wladimir

  2015-02-03 18:15           ` Pieter Wuille
  
From: Alex Morcos @ 2015-02-03 14:30 UTC (permalink / raw)
  To: Wladimir;  Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 1420 bytes --]

Could we see a PR that adds it to BIP 66?   Perhaps we'd all agree quickly
that its so simple we can just add it...
In either case it doesn't seem strictly necessary to me that it was
non-standard before it becomes a soft-fork...


On Tue, Feb 3, 2015 at 7:00 AM, Wladimir <laanwj@gmail•com> wrote:

> > One way to do that is to just - right now - add a patch to 0.10 to
> > make those non-standard. This requires another validation flag, with a
> > bunch of switching logic.
> >
> > The much simpler alternative is just adding this to BIP66's DERSIG
> > right now, which is a one-line change that's obviously softforking. Is
> > anyone opposed to doing so at this stage?
>
> Not opposed, but is kind of late for 0.10, I had hoped to tag rc4 today.
>
> Wladimir
>
>
> ------------------------------------------------------------------------------
> Dive into the World of Parallel Programming. The Go Parallel Website,
> sponsored by Intel and developed in partnership with Slashdot Media, is
> your
> hub for all things parallel software development, from weekly thought
> leadership blogs to news, videos, case studies, tutorials and more. Take a
> look and join the conversation now. http://goparallel.sourceforge.net/
> _______________________________________________
> Bitcoin-development mailing list
> Bitcoin-development@lists•sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bitcoin-development
>

[-- Attachment #2: Type: text/html, Size: 2186 bytes --]

 permalink raw reply	[|nested]
2015-02-03 12:00         ` Wladimir
  2015-02-03 14:30           ` Alex Morcos

  2015-02-03 18:19             ` Gavin Andresen
  2015-02-03 23:38             ` Pieter Wuille
  
From: Pieter Wuille @ 2015-02-03 18:15 UTC (permalink / raw)
  To: Wladimir;  Bitcoin Dev

On Tue, Feb 3, 2015 at 4:00 AM, Wladimir <laanwj@gmail•com> wrote:
>> One way to do that is to just - right now - add a patch to 0.10 to
>> make those non-standard. This requires another validation flag, with a
>> bunch of switching logic.
>>
>> The much simpler alternative is just adding this to BIP66's DERSIG
>> right now, which is a one-line change that's obviously softforking. Is
>> anyone opposed to doing so at this stage?
>
> Not opposed, but is kind of late for 0.10, I had hoped to tag rc4 today.

I understand it's late, which is also why I ask for opinions. It's
also not a priority, but if we release 0.10 without, it will first
need a cycle of making this non-standard, and then in a further
release doing a second softfork to enforce it.

It's a 2-line change; see #5743.

-- 
Pieter



 permalink raw reply	[|nested]
2015-02-03 18:15           ` Pieter Wuille

  2015-02-03 19:22               ` Jeff Garzik
  2015-02-03 23:38             ` Pieter Wuille
  
From: Gavin Andresen @ 2015-02-03 18:19 UTC (permalink / raw)
  To: Pieter Wuille;  Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 500 bytes --]

I think we should just do it, and include it with the other DERSIG changes
for 0.10.

On Tue, Feb 3, 2015 at 1:15 PM, Pieter Wuille <pieter.wuille@gmail•com>
wrote:

>
> I understand it's late, which is also why I ask for opinions. It's
> also not a priority, but if we release 0.10 without, it will first
> need a cycle of making this non-standard, and then in a further
> release doing a second softfork to enforce it.
>
> It's a 2-line change; see #5743.
>
> --
> Pieter
>
>
-- 
--
Gavin Andresen

[-- Attachment #2: Type: text/html, Size: 1027 bytes --]

 permalink raw reply	[|nested]
2015-02-03 18:19             ` Gavin Andresen

  
From: Jeff Garzik @ 2015-02-03 19:22 UTC (permalink / raw)
  To: Gavin Andresen;  Bitcoin Dev

[-- Attachment #1: Type: text/plain, Size: 1498 bytes --]

+1   I just ran an it-works test on #5743.  Not exhaustive, but I do agree
it should be included w/ other DERSIG changes.


On Tue, Feb 3, 2015 at 1:19 PM, Gavin Andresen <gavinandresen@gmail•com>
wrote:

> I think we should just do it, and include it with the other DERSIG changes
> for 0.10.
>
> On Tue, Feb 3, 2015 at 1:15 PM, Pieter Wuille <pieter.wuille@gmail•com>
> wrote:
>
>>
>> I understand it's late, which is also why I ask for opinions. It's
>> also not a priority, but if we release 0.10 without, it will first
>> need a cycle of making this non-standard, and then in a further
>> release doing a second softfork to enforce it.
>>
>> It's a 2-line change; see #5743.
>>
>> --
>> Pieter
>>
>>
> --
> --
> Gavin Andresen
>
>
> ------------------------------------------------------------------------------
> Dive into the World of Parallel Programming. The Go Parallel Website,
> sponsored by Intel and developed in partnership with Slashdot Media, is
> your
> hub for all things parallel software development, from weekly thought
> leadership blogs to news, videos, case studies, tutorials and more. Take a
> look and join the conversation now. http://goparallel.sourceforge.net/
> _______________________________________________
> Bitcoin-development mailing list
> Bitcoin-development@lists•sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bitcoin-development
>
>


-- 
Jeff Garzik
Bitcoin core developer and open source evangelist
BitPay, Inc.      https://bitpay.com/

[-- Attachment #2: Type: text/html, Size: 2619 bytes --]

 permalink raw reply	[|nested]
2015-02-03 18:15           ` Pieter Wuille
  2015-02-03 18:19             ` Gavin Andresen

  
From: Pieter Wuille @ 2015-02-03 23:38 UTC (permalink / raw)
  To: Wladimir;  Bitcoin Dev

On Tue, Feb 3, 2015 at 10:15 AM, Pieter Wuille <pieter.wuille@gmail•com> wrote:
>>> The much simpler alternative is just adding this to BIP66's DERSIG
>>> right now, which is a one-line change that's obviously softforking. Is
>>> anyone opposed to doing so at this stage?

I'm retracting this proposed change.

Suhar Daftuas pointed out that there remain edge-cases which are not
covered (a 33-byte R or S whose first byte is not a zero). The intent
here is really making sure that signature validation and parsing can
be entirely separated, and that signature checking itself does not
need a third return value ("invalid encoding", in addition to "valid
signature" and "invalid signature"). If we don't want to make
assumptions about how that implementation works, the only guaranteed
way of doing that is requiring that R and S are in fact within the
range allowed by secp256k1, which would require an integer decoder
inside the signature encoding checker. I consider that to be
unreasonable.

In addition, a much cleaner solution that covers this as well has
already been proposed: only allow 0 (the empty byte vector) as invalid
signature. That would 100% align signature validity with decoding, and
is much simpler to implement.

-- 
Pieter



 permalink raw reply	[|nested]
2015-01-26 18:35   ` Gregory Maxwell
  2015-01-28  6:24     ` Wladimir

  
From: Pieter Wuille @ 2015-02-06 21:38 UTC ( / )
  To: Gregory Maxwell;  Bitcoin Dev

On Mon, Jan 26, 2015 at 10:35 AM, Gregory Maxwell <gmaxwell@gmail•com> wrote:
>> I'd like to request a BIP number for this.
>
> Sure. BIP0066.

Four implementations exist now:
* for master: https://github.com/bitcoin/bitcoin/pull/5713 (merged)
* for 0.10.0: https://github.com/bitcoin/bitcoin/pull/5714 (merged,
and included in 0.10.0rc4)
* for 0.9.4: https://github.com/bitcoin/bitcoin/pull/5762
* for 0.8.6: https://github.com/bitcoin/bitcoin/pull/5765

The 0.8 and 0.9 version have reduced test code, as many tests rely on
new test framework code in 0.10 and later, but the implementation code
is identical. Work to improve that is certainly welcome.

-- 
Pieter