Mostly trivial, but includes fixes to potential overflows in the ECDSA DER parsers.
Cherry-picked from Zcash PR https://github.com/zcash/zcash/pull/2335
Mostly trivial, but includes fixes to potential overflows in the ECDSA DER parsers.
Cherry-picked from Zcash PR https://github.com/zcash/zcash/pull/2335
0@@ -1,4 +1,5 @@
1 // Copyright (c) 2009-2016 The Bitcoin Core developers
2+// Copyright (c) 2017 The Zcash developers
75 lenbyte--;
76 }
77- if (lenbyte >= sizeof(size_t)) {
78+ static_assert(sizeof(size_t) >= 4, "size_t too small");
79+ if (lenbyte >= 4) {
80 return 0;
This change breaks the intentional compatibility with OpenSSL behavior; it also makes the commit message inaccurately described.
It doesn’t matter for the Bitcoin system anymore because only strict DER matters there now, but this is otherwise an undisclosed consensus change (admittedly where OpenSSL creates inconsistency to begin with).
If lenbyte >= 4
here, then rlen
calculated in the while
loop just below will be >= 224 (because lenbyte
at this point is the length of the representation of rlen
after stripping leading zeros). So this function would in any case return 0 at line 90 if it does not return 0 here: the condition rlen > inputlen - pos
would be true at line 90 since inputlen
is at most the maximum block size, which is less than 224 bytes.
@str4d’s reasoning is not quite correct; the above argument isn’t obvious and doesn’t hold just because of the static_assert
. In any case there is no consensus change (it doesn’t matter whether we return 0 here or at line 90).
If this had actually made a difference, then there would have been a consensus incompatibility between 32-bit and 64-bit platforms, which would surely have been unintended. (I know that originally only 64-bit platforms were supported, but my understanding is that the current code is supposed to support both with no consensus differences.)
Edit: correct 232 to 224. (The most significant byte, which has weight at least 224 when lenbyte >= 4
, must be at least 1.)
If this had actually made a difference, then there would have been a consensus incompatibility between 32-bit and 64-bit platforms, which would surely have been unintended.
It was intended to preserve exact compatibility with OpenSSL which has the same behavior. (particularly during a time when this behavior was embargoed both for us and OpenSSL)
And it very much made a different prior to BIP66, it doesn’t now– but this parsing function exists entirely for compatibility with OpenSSL and would not have been used if Bitcoin used BIP66 since day one.
It didn’t make any difference prior to BIP66, by the argument I gave above – fortunately, because it would have been possible to fork 32-bit nodes from 64-bit nodes otherwise.
It is basically never correct to have a platform-dependent consensus check. In this case, platform-dependent consensus code happened not to result in a difference in behaviour, essentially by luck. IMHO it’s a good idea to clean that up.
114 lenbyte--;
115 }
116- if (lenbyte >= sizeof(size_t)) {
117+ static_assert(sizeof(size_t) >= 4, "size_t too small");
118+ if (lenbyte >= 4) {
119 return 0;
rlen
replaced by slen
, and line 90 replaced by line 129.
46@@ -46,7 +47,7 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1
47 lenbyte = input[pos++];
48 if (lenbyte & 0x80) {
49 lenbyte -= 0x80;
50- if (pos + lenbyte > inputlen) {
51+ if (lenbyte > inputlen - pos) {
52 return 0;
Help me understand how you believe there is a potential overflow here.
My meat based range analysis states: Pos always has value 2 here. Size_t lenbyte is 0-127 inclusive (after the subtraction takes it from a range of 128-255 inclusive). This means that the left side (which is all size_t) has a range of 2-129.
What am I missing? (As an aside, zcash really screwed up if there is any reason to use this lax parser in it; this code exists for compatibility with OpenSSL…)
See https://github.com/zcash/zcash/pull/2335#discussion_r114272216:
This is a different style to before but it’s still overflow-prone (for integer overflows this time).
So this change was included for consistency with the other changes, not necessarily because this specific instance suffers from overflow, but because that style of pointer comparison can be vulnerable, and it is better to have good style precedent. There’s no telling what future people might happen to do with that code snippet.
(As an aside, zcash really screwed up if there is any reason to use this lax parser in it; this code exists for compatibility with OpenSSL…)
In fact, we stripped out this code as unused and unnecessary in https://github.com/zcash/zcash/pull/2360, but for the purposes of upstreaming to Bitcoin Core where it was still present, we intentionally separated that change from these ones.
Thanks for upstreaming this!
utACK d6c7ab9d27a56f08d4072a352640d55d5e46f484
14@@ -15,6 +15,14 @@ Copyright: 2010-2011, Jonas Smedegaard <dr@jones.dk>
15 2011, Matt Corallo <matt@bluematt.me>
16 License: GPL-2+
17
18+Files: src/secp256k1/build-aux/m4/ax_jni_include_dir.m4
15@@ -15,10 +16,12 @@
16
17 /**
18 * secp256k1:
19- * const unsigned int PRIVATE_KEY_SIZE = 279;
20- * const unsigned int PUBLIC_KEY_SIZE = 65;
21- * const unsigned int SIGNATURE_SIZE = 72;
22- *
23+ */
24+const unsigned int PUBLIC_KEY_SIZE = 65;
17@@ -17,17 +18,18 @@
18
19 /**
20 * secp256k1:
21- * const unsigned int PRIVATE_KEY_SIZE = 279;
22- * const unsigned int PUBLIC_KEY_SIZE = 65;
23- * const unsigned int SIGNATURE_SIZE = 72;
24- *
25+ */
26+const unsigned int PRIVATE_KEY_SIZE = 279;