Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

supplying invalid blinding factors to rawblindrawtransaction causes node to crash #540

Closed
dgpv opened this issue Mar 27, 2019 · 16 comments · Fixed by #653 or #661
Closed

supplying invalid blinding factors to rawblindrawtransaction causes node to crash #540

dgpv opened this issue Mar 27, 2019 · 16 comments · Fixed by #653 or #661
Labels

Comments

@dgpv
Copy link
Contributor

dgpv commented Mar 27, 2019

if invalid value blinder is supplied, this assert is triggered:

assert(ret);

if invalid asset blinder is supplied, this CHECK() in secp256k1 is triggered:

Triggered with this small patch to qa/rpc-tests/confidential_transactions.py (tested on 0.14.1 branch, but the failing code is the same on master. to trigger first case, you just put ab in place of value blinder, not asset blinder as in this patch):

diff --git a/qa/rpc-tests/confidential_transactions.py b/qa/rpc-tests/confidential_transactions.py
index 61393fc6e..93c7b5cc5 100755
--- a/qa/rpc-tests/confidential_transactions.py
+++ b/qa/rpc-tests/confidential_transactions.py
@@ -470,6 +470,12 @@ class CTTest (BitcoinTestFramework):
         except JSONRPCException:
             pass
 
+        try:
+            ab = 'FF'*32
+            blindtx = self.nodes[0].rawblindrawtransaction(rawtx, [unspent[0]["blinder"], unspent[1]["blinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], ab])
+        except JSONRPCException as e:
+            print(e)
+
         blindtx = self.nodes[0].rawblindrawtransaction(rawtx, [unspent[0]["blinder"], unspent[1]["blinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]])
         signtx = self.nodes[0].signrawtransaction(blindtx)
         txid = self.nodes[0].sendrawtransaction(signtx["hex"])

While the impact of the bug is not very serious, as this can only be triggered with authenticated rpc request and results only in node crash, I believe the crash is not correct response to invalid user input.

First case can be fixed by checking ret and returning failure, instead of dying on assert.

But the second case is not that easy, and would require checking that a given blinder represents valid scalar before passing it to secp256k1_generator_generate_blinded. I did not find appropriate function in secp256k1 that would allow to check this condition before passing the data to secp256k1_generator_generate_blinded.

@instagibbs
Copy link
Collaborator

Interesting report, thanks! Will look and see if we need to get support upstream in secp-zkp.

@instagibbs instagibbs added the bug label Mar 27, 2019
@instagibbs
Copy link
Collaborator

@apoelstra says secp256k1_ec_seckey_verify is what we want.

@dgpv
Copy link
Contributor Author

dgpv commented Mar 27, 2019

If CHECK is removed in upstream secp-zkp, then pre-verification would not be needed. OTOH, that means there would be delay in fixing the bug, so maybe it should be fixed with pre-verification

@dgpv
Copy link
Contributor Author

dgpv commented Mar 27, 2019

secp256k1_ec_seckey_verify also checks for zero scalar, but some code in wallet.cpp passes zero asset blinders to BlindTransaction(), so in addition to secp256k1_ec_seckey_verify there would need to be a check for zero scalar (probably, memcmp with static array of nuls), in that case it should not fail.

@instagibbs
Copy link
Collaborator

oof ok. That's annoying.

@dgpv
Copy link
Contributor Author

dgpv commented Mar 27, 2019

Maybe it is better to just wait for remove of the CHECK in upstream secp-zkp, considering that the impact of the bug is small

@dgpv
Copy link
Contributor Author

dgpv commented Mar 27, 2019

Just for reference, a patch that fixes this on 0.14 branch (not doing PR because 0.14 is outdated, but I don't have time now to properly build and test on master)

diff --git a/src/blind.cpp b/src/blind.cpp
index 10fd46454..a8d3f50db 100644
--- a/src/blind.cpp
+++ b/src/blind.cpp
@@ -247,6 +247,7 @@ int BlindTransaction(std::vector<uint256 >& input_blinding_factors, const std::v
 
     int ret;
     int nBlindAttempts = 0, nIssuanceBlindAttempts = 0, nSuccessfullyBlinded = 0;
+    static const unsigned char zero_scalar[32] = {0};
 
     //Surjection proof prep
 
@@ -278,6 +279,10 @@ int BlindTransaction(std::vector<uint256 >& input_blinding_factors, const std::v
                 return -1;
             }
         } else {
+            if (secp256k1_ec_seckey_verify(secp256k1_blind_context, input_asset_blinding_factors[i].begin()) != 1 &&
+                memcmp(zero_scalar, input_asset_blinding_factors[i].begin(), 32) != 0 ) {
+		return -1;
+            }
             ret = secp256k1_generator_generate_blinded(secp256k1_blind_context, &targetAssetGenerators[totalTargets], input_assets[i].begin(), input_asset_blinding_factors[i].begin());
             assert(ret == 1);
         }
@@ -396,7 +401,6 @@ int BlindTransaction(std::vector<uint256 >& input_blinding_factors, const std::v
 
 
     //Running total of newly blinded outputs
-    static const unsigned char diff_zero[32] = {0};
     unsigned char blind[nToBlind][32];
     unsigned char asset_blind[nToBlind][32];
     secp256k1_pedersen_commitment commit;
@@ -512,7 +516,9 @@ int BlindTransaction(std::vector<uint256 >& input_blinding_factors, const std::v
 
                 // Generate value we intend to insert
                 ret = secp256k1_pedersen_blind_generator_blind_sum(secp256k1_blind_context, &blindedAmounts[0], &assetblindptrs[0], &blindptrs[0], nBlindAttempts + nBlindsIn, nIssuanceBlindAttempts + nBlindsIn);
-                assert(ret);
+                if(ret != 1) {
+                    return -1;
+                }
 
                 // Resulting blinding factor can sometimes be 0
                 // where inputs are the negations of each other
@@ -522,7 +528,7 @@ int BlindTransaction(std::vector<uint256 >& input_blinding_factors, const std::v
                 // becomes just (r'), if this is 0, we can just
                 // abort and not blind and the math adds up.
                 // Count as success(to signal caller that nothing wrong) and return early
-                if (memcmp(diff_zero, &blind[nBlindAttempts-1][0], 32) == 0) {
+                if (memcmp(zero_scalar, &blind[nBlindAttempts-1][0], 32) == 0) {
                    return ++nSuccessfullyBlinded;
                 }
             }

@apoelstra
Copy link
Member

The check is removed upstream; we could just update the version of secp256k1-zkp in elements now.

stevenroose added a commit that referenced this issue Jun 7, 2019
c866f52 Squashed 'src/secp256k1/' changes from 43dd1f4..44db4d8 (Gregory Sanders)

Pull request description:

  Resolves #540 along with other minor updates.

Tree-SHA512: ef34626cb6a5b35c81587d3bf545baab5dbe875db61047fbccfc367058c5416f8e32e5ff6b1ffbb0d14c0e5307893376b0d4b548846994470c8191fe295538ee
@dgpv
Copy link
Contributor Author

dgpv commented Jun 7, 2019

The updated secp-zkp fixes the crash via CHECK, what is within the secp lib, but the asserts in Elements code are still there,

assert(ret);
will cause crash if invalid value blinder is supplied via RPC, and
assert(ret == 1);
that will cause crash if invalid asset blinder is supplied

I think the asserts should be replaced with checks that return -1 if ret is 0

@instagibbs
Copy link
Collaborator

instagibbs commented Jun 7, 2019 via email

@dgpv
Copy link
Contributor Author

dgpv commented Jun 10, 2019

@stevenroose can you reopen this, please ? The original issue is not yet resolved

@stevenroose stevenroose reopened this Jun 11, 2019
@stevenroose
Copy link
Member

I'm adding that test case to the functional test. I agree we shouldn't assert validity of keys when we get them from the user..

@instagibbs
Copy link
Collaborator

@stevenroose thanks!

@stevenroose
Copy link
Member

So #661 covers those two asserts and those are indeed the ones hit when providing incorrect blinders.

The blind.cpp file is full of asserts, most of them seem to be from self-generated information, though. Luckily that file is only exposed to RPC users.

@instagibbs
Copy link
Collaborator

UnblindConfidentialPair is exposed via wallet scanning.

@stevenroose
Copy link
Member

Yeah UnblindConfidentialPair is the only one without asserts :)

instagibbs added a commit that referenced this issue Jun 21, 2019
f9d159b Don't assert on user-inputed values (Steven Roose)

Pull request description:

  This prevents the assertion from crashing the node when an RPC user
  enters invalid blinding factors.

  Fixes #540.

Tree-SHA512: d68787edb9919a3a55b94884180700854941c5f273c2ac07f534bd0a3981f163fa26c8cef931001bc9906d6c74a58cdc0148eb7d53d3edbedf005a0be7c60c5b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants