Return-Path: Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 5E4F5C0881 for ; Thu, 28 Nov 2019 08:27:46 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id 43419203B2 for ; Thu, 28 Nov 2019 08:27:46 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zhbe2rjMLNiW for ; Thu, 28 Nov 2019 08:27:45 +0000 (UTC) X-Greylist: delayed 00:20:35 by SQLgrey-1.7.6 Received: from azure.erisian.com.au (cerulean.erisian.com.au [139.162.42.226]) by silver.osuosl.org (Postfix) with ESMTPS id E1154203AC for ; Thu, 28 Nov 2019 08:27:44 +0000 (UTC) Received: from aj@azure.erisian.com.au (helo=sapphire.erisian.com.au) by azure.erisian.com.au with esmtpsa (Exim 4.89 #1 (Debian)) id 1iaEpL-0007rO-OP; Thu, 28 Nov 2019 18:07:05 +1000 Received: by sapphire.erisian.com.au (sSMTP sendmail emulation); Thu, 28 Nov 2019 18:06:59 +1000 Date: Thu, 28 Nov 2019 18:06:59 +1000 From: Anthony Towns To: Russell O'Connor , Bitcoin Protocol Discussion Message-ID: <20191128080659.msrpdpcjhhvbqtv2@erisian.com.au> References: MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: NeoMutt/20170113 (1.7.2) X-Spam-Score-int: -8 X-Spam-Bar: / Cc: Pieter Wuille Subject: Re: [bitcoin-dev] Signing CHECKSIG position in Tapscript X-BeenThere: bitcoin-dev@lists.linuxfoundation.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Bitcoin Protocol Discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 28 Nov 2019 08:27:46 -0000 On Wed, Nov 27, 2019 at 04:29:32PM -0500, Russell O'Connor via bitcoin-dev wrote: > The current tapscript proposal requires a signature on the last executed > CODESEPRATOR position.  I'd like to propose an amendment whereby instead of > signing the last executed CODESEPRATOR position, we simply always sign the > position of the CHECKSIG (or other signing opcode) being executed. FWIW, there's discussion of this at http://www.erisian.com.au/taproot-bip-review/log-2019-11-28.html#l-65 > However, unless CODESEPARATOR is explicitly used, there is no protection > against these sorts of attacks when there are multiple participants that have > signing conditions within a single UTXO (or rather within a single tapleaf in > the tapscript case). (You already know this, but:) With taproot key path spending, the only other conditions that can be placed on a transaction are nSequence, nLockTime, and the annex, all of which are committed to via the signature; so I think this concern only applies to taproot script path spending. The proposed sighashes for taproot script path spending all commit to the script being used, so you can't reuse the signature in a different leaf of the merkle tree of scripts for the UTXO, only in a separate execution path within the script you're already looking at. > So for example, if Alice and Bob are engaged in some kind of multi-party > protocol, and Alice wants to pre-sign a transaction redeeming a UTXO but > subject to the condition that a certain hash-preimage is revealed, she might > verify the Script template shows that the code path to her public key enforces > that the hash pre-image is revealed (using a toolkit like miniscript can aid in > this), and she might make her signature feeling secure that it, if her > signature is used, the required preimage must be revealed on the blockchain.  > But perhaps Bob has masquated Alice's pubkey as his own, and maybe he has > inserted a copy of Alice's pubkey into a different path of the Script > template. > > Now Alice's signature can be copied and used in this alternate path, > allowing the UTXO to be redeemed under circumstances that Alice didn't believe > she was authorizing.  In general, to protect herself, Alice needs to inspect > the Script to see if her pubkey occurs in any other branch.  Given that her > pubkey, in principle, could be derived from a computation rather that pushed > directly into the stack, it is arguably infeasible for Alice to perform the > required check in general. First, it seems like a bad idea for Alice to have put funds behind a script she doesn't understand in the first place. There's plenty of scripts that are analysable, so just not using ones that are too hard to analyse sure seems like an option. Second, if there are many branches in the script, it's probably more efficient to do them via different branches in the merkle tree, which at least for this purpose would make them easier to analyse as well (since you can analyse them independently). Third, if you are doing something crazy complex where a particular key could appear in different CHECKSIG operators and they should have independent signatures, that seems like you're at the level of complexity where learning about CODESEPARATOR is a reasonable thing to do. I think CODESEPARATOR is a better solution to this problem anyway. In particular, consider a "leaf path root OP_MERKLEPATHVERIFY" opcode, and a script that says "anyone in group A can spend if the preimage for X is revelaed, anyone in group B can spend unconditionally": IF HASH160 x EQUALVERIFY groupa ELSE groupb ENDIF MERKLEPATHVERIFY CHECKSIG spendable by siga keya path preimagex 1 or sigb keyb path 0 With your proposed semantics, if my pubkey is in both groups, my signature will sign for position 10, and still be valid on either path, even if the signature commits to the CHECKSIG position. I could fix my script either by having two CHECKSIG opcodes (one for each branch) and also duplicating the MERKLEPATHVERIFY; or I could add a CODESEPARATOR in either IF branch. (Or I could just not reuse the exact same pubkey across groups; or I could have two separate scripts: "HASH160 x EQUALVERIFY groupa MERKLEPATHVERIFY CHECKSIG" and "groupb MERKLEPATHVERIFY CHECKSIG") > I believe that it would be safer, and less surprising to users, to always sign > the CHECKSIG position by default. > As a side benefit, we get to eliminate CODESEPARATOR, removing a fairly awkward > opcode from this script version. As it stands, ANYPREVOUTANYSCRIPT proposes to not sign the script code (allowing the signature to be reused in different scripts) but does continue signing the CODESEPARATOR position, allowing you to optionally restrict how flexibly you can reuse signatures. That seems like a better tradeoff than having ANYPREVOUTANYSCRIPT signatures commit to the CHECKSIG position which would make it a fair bit harder to design scripts that can share signatures, or not having any way to restrict which scripts the signature could apply to other than changing the pubkey. A hypothetical alternate "codeseparator" design: when script execution starts, initialise an empty byte string "trace"; each time an opcode is executed append "0xFF"; each time an opcode is skipped append "0x00". When a CODESEPARATOR is seen, calculate sha256(trace) and store it, everytime a CHECKSIG is executed, include the sha256(trace) from the last CODESEPARATOR in the digest [0]. That should make each checksig commit to the exact path the script took up to the last CODESEPARATOR seen. I think it's probably more complex than is really useful though, so I'm not proposing it seriously. [0] If there's not been a CODESEPARATOR, then sha256(trace)=sha256(""); if there's been one CODESEPARATOR and it was the first opcode seen, sha256(trace)=sha256("\xff"). Cheers, aj