Return-Path: Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id B3FC8C000A for ; Sun, 11 Apr 2021 16:45:17 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 9F66240284 for ; Sun, 11 Apr 2021 16:45:17 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org X-Spam-Flag: NO X-Spam-Score: -1.224 X-Spam-Level: X-Spam-Status: No, score=-1.224 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_SOFTFAIL=0.665, T_KAM_HTML_FONT_INVALID=0.01] autolearn=no autolearn_force=no Authentication-Results: smtp4.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=nunchuk-io.20150623.gappssmtp.com Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dXReHC_0vh-L for ; Sun, 11 Apr 2021 16:45:15 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by smtp4.osuosl.org (Postfix) with ESMTPS id F3C9A4027F for ; Sun, 11 Apr 2021 16:45:14 +0000 (UTC) Received: by mail-pg1-x535.google.com with SMTP id y32so7505407pga.11 for ; Sun, 11 Apr 2021 09:45:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nunchuk-io.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=RXr4VaPOizdqeFrh4VluRkroIHMe/TedsL9bXk7QNmA=; b=LNpG79HAg9rjQ4Pqz4GZsnfmbggfICouNLIAKwZjhjn9SfWcdktmRZpPvxdZnz0BbI 8dUAi9HY1yhU1Te1s56a7I73QKfCCsIGTcS695xVD6KS5GwYS7bRXn/tbMJJnZPY37OF TWJyPJFPzvmU0fYpQeFSe9dRVO+JC2deltpbr6FCiMEfIqooHMFTYo9+JdcNjQpNLMUo N4nDEgYgWkWln8V5kZ5XFTENNLV6GvT/gIOW5tZ8DyhnNsTJ4u0fzIROw+iniIQJqOGl kk+Wc1VCcxUFTIQ9bnSEP1jj/mIjE9mdG+pzsS81vJ4i5Tus32H20SD9g/Iejcsl5Kwx X6/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=RXr4VaPOizdqeFrh4VluRkroIHMe/TedsL9bXk7QNmA=; b=sY0IiHm4DydhAAzCLucTo8z2yOQSqP6Ajq4BbVOklFcEZyPmY6otl0ip7u9ovtFn51 /r+8ZGwDdDuddG1yV3eMBrwS/mNHk6l8jDG4tQ1nFp4MEbryOtPyw4anUJUDltNxxvnz qTbdHZL5360iMyIwkM/W/ezyOayrWwDkufl7dolBWEdaTGXNdN2lumLSKU8JjksKjXtC BusVa5V300OGB/XA7ecvdj0i33+oPcjU0GhXAOBwj1AxiXBQoXnnypxdCujxDqz/+R2m KahpR671q8OmT9aKPxYmhlXQfsZCTv1HhGM+woax3qN2dYH8ncIp2SMrp40hmEMYDVZx 06xw== X-Gm-Message-State: AOAM5301LiYK0qmsDyJkW5NDBl7Oe+MJ+XOSqUygpgmkCaYjsQtzTO4a Cw0Ng42NK2/pUtZNYbsDvb+ivw3IeZES29oDwfzA4g== X-Google-Smtp-Source: ABdhPJx+WB2HIy36EAogFwmfaBT750a7LLWqfLbBB2w7O4NzQC7H/5ZIWYlUFxzOXMneJuui7CeyBUtS2PxzEBwKsh4= X-Received: by 2002:a63:d43:: with SMTP id 3mr22938208pgn.5.1618159514356; Sun, 11 Apr 2021 09:45:14 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Hugo Nguyen Date: Sun, 11 Apr 2021 09:45:00 -0700 Message-ID: To: "Michael.flaxman" Content-Type: multipart/alternative; boundary="0000000000000b20c905bfb51f60" X-Mailman-Approved-At: Sun, 11 Apr 2021 17:11:03 +0000 Cc: Bitcoin Protocol Discussion Subject: Re: [bitcoin-dev] Proposal: Bitcoin Secure Multisig Setup 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: Sun, 11 Apr 2021 16:45:17 -0000 --0000000000000b20c905bfb51f60 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Michael, Comments inline. On Sat, Apr 10, 2021 at 7:34 PM Michael.flaxman < michael.flaxman@protonmail.com> wrote: > Hi Hugo, > > I appreciate the effort you and everyone else is making to improve > multisig in bitcoin! > Thanks. > I like that this BIP gets rid of SLIP132 version bytes, as those have bee= n > de-facto deprecated in favor of output descriptors for some time. Having = a > standard for how to communicate descriptor records (BSMS 1.0) also seems > like a nice positive. > > The most commonly raised issues from the 10x security guide > are about how to properly verify that all > hardware wallets are participants in the user's multisig quorum (and with > the correct m-of-n). This shows up in two big ways: > > 1. The O(n^2) xpub validation problem creates a bad UX and is hard for > non-advanced users. > 2. The risk for stateless hardware wallets (like Trezor) to have their > xpubs swapped out by a compromised Coordinator. > > Unfortunately, this BIP does not improve either of these issues, while > adding considerable complexity. > > *1. O(n^2) Xpub Validation* > > The proposed use of an output descriptor checksum has an obvious 40-bit > MITM collision attack. A compromised Coordinator could trick a Signer int= o > displaying an attacker's receive address, despite a correctly functioning > Signers and the user properly validating the checksum (github link > ). > > Using a checksum with much higher entropy would reduce xpub validation to > O(n) and create a very nice UX for signers. This would be a huge win for > multisig! Instead, the recommended solution from the BIP is to validate a= ll > the key records manually, which is how multisig is currently done and wha= t > we desperately want to move away from. With a proper checksum, there=E2= =80=99s no > reason for a user to ever see an xpub. > > Users should not be shown a checksum and asked to validate it in meatspac= e > (across Signers) if an attacker=E2=80=99s address could still be substitu= ted! > Validating a single address across devices does solve this problem, but i= f > you=E2=80=99re going to validate an address there=E2=80=99s no reason to = display the > checksum at all. However, validating an address is confusing to non-exper= ts: > > - Is it a wallet ID or a bitcoin address? > - Am I supposed to send funds to this address? > > If creating a new checksum standard for the descriptor record is > undesirable, we could use a child address (from an unhardened BIP32 path) > and encode that in some way for end-users to verify it matches across all > Signers. It would be strongly preferable for the encoding to be an > unambiguously different format from a bitcoin address / BIP39 seed phrase= , > so that it=E2=80=99s clear it=E2=80=99s just a wallet ID. One non-ideal b= ut simple solution > is to use a hash function (i.e. dsha256) to calculate the digest of the > child address, and display this in hexadecimal format. While hexadecimal = is > non-ideal for manual verification, it is already trivial for any bitcoin > library to perform these steps. > As I have responded to your previous comment about the same on Github ( https://github.com/nunchuk-io/bips/pull/1), I do see the value of a longer checksum. There are trade-offs when it comes to designing checksum. Mainly complexity and size. At one end of the spectrum you can have a single-byte XOR checksum. At the other end you can have something like HMAC-SHA256 (which we are using in the proposal to calculate the MACs for the key and descriptor records). And then there's everything in between. But we should know that nothing comes for free. It's a good topic that warrants further discussion. Confirming a single address is a promising direction, since it's something the user should do anyway prior to using the wallet. Currently the proposal recommends that the Signers show a preview of the first address(es) upon wallet creation. But we can elevate this and make it a mandatory part of the spec: have all Signers confirm that they have the same 1st receive address. If we go with this approach, the checksum can stay as-is, and only there for error detection. (We get the checksum for free with the descriptor language anyway, so there's no reason to remove it). Also nice to see that you have come around and agree that moving away from manual inspection is desirable. > *2. Allow Support for Stateless Wallets* > > The current BIP states: > > * "If all checks pass, the Signer must persist the descriptor record in > its storage."* > > While persistence has a lot of benefits, it is not a feature of the most > sold multisig hardware wallet: Trezor. A simple solution here is to have > each Signer sign the entire descriptor record at the end of round 2, not > just its own key record in round 1. Then the data can be stored anywhere > (including on the Signer itself) and played back to each Signer for > validation when needed. The end-user would have no idea this was happenin= g, > but the device could refuse to display information it hasn=E2=80=99t full= y > validated (or at least add a warning message). Even a device with > persistent storage would be better served using a signature, so that an > evil maid couldn't tamper with the device (say in the no-encryption case > for simplicity). > I reiterate that I strongly disagree that going stateless is the direction we want to pursue when it comes to multisig. In a multisig or any type of MPC smart contract, any Signer in the contract must know who the other co-Signers are at all times. You can choose to do this verification once at setup and persist this info on the Signer, or you'd have to re-do the verification for every single transaction. There is no other choice. Signing the descriptor record is insufficient, while also introducing a great deal of complexity. Here are the problems: 1) The signature needs to be stored somewhere. Who stores it if it's not the Signer itself? What if it gets lost? (If the Signer stores its own signature, then the scheme is no longer stateless. You might as well store the full descriptor). 2) When the signature is "played back" to the Signer, a copy of the original descriptor must be included. Who stores the descriptor? What if it gets lost? This is an under-appreciated aspect of the stateful approach: every participant in the multisig has a full copy of the original contract, which adds resilience to the wallet backup / recovery process. 3) Because the full descriptor must be "played back" for every single transaction, this means every detail of the contract must be shared again and again, indefinitely. Not only does this add overhead (engineering and cognitive) to the spending process, it has massive privacy implications, since the descriptor contains everything you need to know about the wallets and its participants. Here's an analogy in the physical world. Would you: a) Enter any type of written contract and b) Not keep a copy of the contract, forget about it, and c) Later on rely on your counter-parties or a third-party to provide you with the original contract and your signature, when the terms get carried out? One would be insane to enter such a contract in the real world. I realize that some vendors are currently not stateful, but I take this as an unfortunate fact, because multisig wasn't a priority when these hardware were originally designed. But that is no reason to keep going with a broken architecture. The industry is green enough that we still can learn and recover from these sorts of flaws. Since you mentioned Trezor, I want to thank Pavol in particular here, because as Trezor CTO Pavol knows best that Trezor is currently stateless, but he's still on-board with the general idea here, AFAIU. Bottom line: IMO, signers in a multisig MUST be stateful. This existing vulnerability in stateless wallets is particularly bad for > hosted multisig services like Casa/Unchained, where the service might > control m-1 keys. It=E2=80=99s far easier for a hosted service to potenti= ally trick > non-expert users into displaying an attacker's receive address on their > stateless Signer. > > For example, assume the user is doing 2-of-3 multisig, where the > Coordinator (service) controls 1 key. Here is how the Coordinator could > trick their end-users: > > 1. Coordinator swaps out 1 of the end-user=E2=80=99s xpubs, going from= a > 2-of-3 where the end-user has 2 seeds to a 2-of-3 where the Coordinato= r has > 2 seeds. > 2. The end-user logs into the service to get a new receive address, > and the service (Coordinator) displays malicious receive address X (as= part > of a 2-of-3). > 3. The end user connects stateless Signer 1 to the service > (Coordinator), which under-the-hood gives stateless Signer 1 proof tha= t it > is included in this 2-of-3. Stateless singer 1 displays malicious rece= ive > address X! > 4. The end-user doesn't verify the address on Signer 2, as many users > unfortunately don't -- perhaps it is in a far away location and the > end-user (incorrectly) thinks that it=E2=80=99s already been validated= in 2 places > -- and makes a large deposit to receive address X. These funds now bel= ong > to the attacker and can be swept at any time! > > If stateless Signer 1 required a signature to be replayed at step 3, > stateless Signer 1 would refuse to display malicious receive address X (o= r > at a minimum warn the end-user that it did not have enough info to proper= ly > validate the address). > > This is also a concern for self-hosted multisig, I just used the hosted > services as the best example. > > It's also not just Trezor that is stateless. For example, I wrote a > simple CLI software multisig wallet as part of the buidl library > to be used > mostly for emergency recovery. At 800 lines of code, it's too > simple/minimal to touch the file system. > > *BIP39* > > While unrelated, the use of BIP39 words for session tokens seems like a > big mistake, as end-users have learned over years that BIP39 words are fo= r > private key material. A small percent of users may backup their token BIP= 39 > mnemonic and not their seed phrase BIP39 mnemonic! My suggestion is to ju= st > stick with the other two Token options: decimal and hex. > Repost of my previous response. We discussed this at length on the linked Github PR: "We decided to keep the TOKEN at 6-9 words, not 12 or anything above precisely for this reason. Please note that the user has to back up their Signers first, before proceeding to setting up the multisig wallet. So there's no writing both things down at once or mixing of the two flows here= . I also find it hard to believe that someone who wants to invest in a safe multisig solution (and therefore must know at minimum what keys and multisig represent) will not know the difference between (permanent) 12 words and (one-time use) 6 words. Also note that the TOKEN can be used without using BIP39 mnemonic at all." We also made the decimal format, not BIP39 mnemonic, the recommended encoding in the spec. Best, Hugo --0000000000000b20c905bfb51f60 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Michael,
Comments inline.

=
On Sat, Ap= r 10, 2021 at 7:34 PM Michael.flaxman <michael.flaxman@protonmail.com> wrote:
Hi Hugo,
=
I appreciate the effort you and everyone else is making to i= mprove multisig in bitcoin!

Thanks.
=C2= =A0
I like tha= t this BIP gets rid of SLIP132 version bytes, as those have been de-facto d= eprecated in favor of output descriptors for some time. Having a standard f= or how to communicate descriptor records (BSMS 1.0) also seems like a nice = positive.

The most commonly raised issues from= the 10x security guide=C2=A0are = about how to properly verify that all hardware wallets are participants in = the user's multisig quorum (and with the correct m-of-n). This shows up= in two big ways:
  1. The O(n^2) xpub validation problem creat= es a bad UX and is hard for non-advanced users.
  2. The risk for st= ateless hardware wallets (like Trezor) to have their xpubs swapped out by a= compromised Coordinator.
Unfortunately, this BIP does no= t improve either of these issues, while adding considerable complexity.
=

1. O(n^2) Xpub Validation<= br>


The proposed use of an output descriptor checksum has an obv= ious 40-bit MITM collision attack. A compromised Coordinator could trick a = Signer into displaying an attacker's receive address, despite a correct= ly functioning Signers and the user properly validating the checksum (github link).


Using a checksum with much higher entropy would reduce = xpub validation to O(n) and create a very nice UX for signers. This would b= e a huge win for multisig! Instead, the recommended solution from the BIP i= s to validate all the key records manually, which is how multisig is curren= tly done and what we desperately want to move away from. With a proper chec= ksum, there=E2=80=99s no reason for a user to ever see an xpub.


Users should not be shown a checksum and as= ked to validate it in meatspace (across Signers) if an attacker=E2=80=99s a= ddress could still be substituted! Validating a single address across devic= es does solve this problem, but if you=E2=80=99re going to validate an addr= ess there=E2=80=99s no reason to display the checksum at all. However, vali= dating an address is confusing to non-experts:<= br>

  • Is it a wallet ID or a bitcoin address?
  • <= span style=3D"font-size:11pt">Am I supposed to send funds to this address?<= /span>

If creating a new checksum standard for t= he descriptor record is undesirable, we could use a child address (from an = unhardened BIP32 path) and encode that in some way for end-users to verify = it matches across all Signers. It would be strongly preferable for the enco= ding to be an unambiguously different format from a bitcoin address / BIP39= seed phrase, so that it=E2=80=99s clear it=E2=80=99s just a wallet ID. One= non-ideal but simple solution is to use a hash function (i.e. dsha256) to= calculate the digest of the child address, and display this in hexadecimal= format. While hexadecimal is non-ideal for manual verification, it is alre= ady trivial for any bitcoin library to perform these steps.


As I have responded to your previous = comment about the same on Github (https://github.com/nunchuk-io/bips/pull/1), I do see the= =C2=A0value of a longer=C2=A0checksum.

There are trade-offs when it = comes to designing checksum. Mainly complexity and size. At one end of the = spectrum you can have a single-byte XOR checksum. At the other end you can = have something like HMAC-SHA256 (which we are using in the proposal to calc= ulate the MACs for the key and descriptor records). And then there's ev= erything in between. But we should know=C2=A0that nothing comes for free.
It's a good topic that=C2=A0warrants further discussion.

C= onfirming a single address is a promising direction,=C2=A0since it's so= mething the user should do anyway prior to using the wallet. Currently the = proposal recommends that the Signers show a preview of the first address(es= ) upon wallet creation. But we can elevate this and make it a mandatory par= t of the spec: have all Signers confirm that they have the same 1st receive= address. If we go with this approach, the checksum can stay as-is,=C2=A0an= d only there for error detection. (We get the checksum for free with the de= scriptor language anyway, so there's no reason to remove it).

Al= so nice to see that you have come around and agree that moving away from ma= nual inspection is desirable.=C2=A0


2. Allow Support for Stateless Wallets


The current BIP states:


=C2=A0"If all check= s pass, the Signer must persist the descriptor record in its storage."=


While persistence has a lot= of benefits, it is not a feature of the most sold multisig hardware wallet= : Trezor. A simple solution here is to have each Signer sign the entire des= criptor record at the end of round 2, not just its own key record in round = 1. Then the data can be stored anywhere (including on the Signer itself) an= d played back to each Signer for validation when needed. The end-user would= have no idea this was happening, but the device could refuse to display in= formation it hasn=E2=80=99t fully validated (or at least add a warning mess= age). Even a device with persistent storage would be better served using a = signature, so that an evil maid couldn't tamper with the device (say in= the no-encryption case for simplicity).


I reiterate that I strongly disagree that going stateles= s is the direction we want to pursue when it comes to multisig.

In a= multisig or any type of MPC smart contract, any Signer in the contract mus= t know who the other co-Signers are at all times. You can choose to do this= verification once at setup and persist this info on the Signer, or you'= ;d have to re-do the verification for every single transaction. There is no= other choice.

Signing the=C2=A0descriptor record is insufficient, w= hile also=C2=A0introducing a great deal of complexity. Here are the problem= s:
1) The signature needs to be stored somewhere. Who stores it if it= 9;s not the Signer itself? What if it gets lost? (If the Signer stores its = own signature, then the scheme is no longer stateless. You might as well st= ore the=C2=A0full descriptor).
2) When the signature is "played bac= k" to the Signer, a copy of the original descriptor must be included. = Who stores the descriptor? What if it gets lost? This is an under-appreciat= ed aspect of the stateful approach: every participant in the multisig has a= full copy of the original contract, which adds resilience to the wallet ba= ckup / recovery process.
3) Because the full descriptor must be "pl= ayed back" for every single transaction, this means every detail of th= e contract must be shared again and again,=C2=A0indefinitely. Not only does= this add overhead (engineering and cognitive) to the spending process, it = has massive privacy implications, since the descriptor contains everything = you need to know about the wallets and its participants.

Here's = an analogy in the physical world. Would you:
a) Enter any type of writte= n contract and
b)=C2=A0Not keep a copy of the contract, forget about it,= =C2=A0and
c) Later on rely on your counter-parties or a third-party to = provide you with the original contract and your signature, when the terms g= et carried out?

One would be insane to enter such a contract in the = real world.

I realize that some vendors are currently not stateful, = but I take this as an unfortunate fact, because multisig wasn't a prior= ity when these hardware were originally designed. But that is no reason to = keep going with a broken architecture. The industry is green enough that we= still can learn and recover from these sorts of flaws.

Since you me= ntioned Trezor, I want to thank Pavol in particular here, because as Trezor= CTO Pavol knows best that Trezor is currently stateless, but he's stil= l on-board with the general idea here, AFAIU.=C2=A0

Bottom line: IMO= , signers in a multisig MUST be stateful.

= This existing vulnerability in stateless wallets is = particularly bad for hosted multisig services like Casa/Unchained, where th= e service might control m-1 keys. It=E2=80=99s far easier for a hosted serv= ice to potentially trick non-expert users into displaying an attacker's= receive address on their stateless Signer.
=


For example, assume the user is doing 2-of-3 multisig, where th= e Coordinator (service) controls 1 key. Here is how the Coordinator could t= rick their end-users:

  1. Coord= inator swaps out 1 of the end-user=E2=80=99s xpubs, going from a 2-of-3 whe= re the end-user has 2 seeds to a 2-of-3 where the Coordinator has 2 seeds.<= /span>
  2. The end-user logs into the ser= vice to get a new receive address, and the service (Coordinator) displays m= alicious receive address X (as part of a 2-of-3).
  3. The end user connects stateless Signer 1 to the service= (Coordinator), which under-the-hood gives stateless Signer 1 proof that it= is included in this 2-of-3. Stateless singer 1 displays malicious receive = address X!
  4. The end-user doesn= 't verify the address on Signer 2, as many users unfortunately don'= t -- perhaps it is in a far away location and the end-user (incorrectly) th= inks that it=E2=80=99s already been validated in 2 places -- and makes a la= rge deposit to receive address X. These funds now belong to the attacker an= d can be swept at any time!

If stateless = Signer 1 required a signature to be replayed at step 3, stateless Signer 1 = would refuse to display malicious receive address X (or at a minimum warn t= he end-user that it did not have enough info to properly validate the addre= ss).


This is also a concern for self-hosted multisig, I just used the hosted s= ervices as the best example.

It's also not just Trezor that is stateles= s. For example, I wrote a simple CLI software multisig w= allet as part of the buidl library=C2=A0to be used mostly for emergency= recovery. At 800 lines of code, it's too simple/minimal to touch the f= ile system.

BIP39

While unrelated, the use of BIP39 words for session tokens seems lik= e a big mistake, as end-users have learned over years that BIP39 words are = for private key material. A small percent of users may backup their token B= IP39 mnemonic and not their seed phrase BIP39 mnemonic! My suggestion is to= just stick with the other two Token options: decimal and hex.


Repost of my previous response. We= discussed this at length on the linked=C2=A0Github PR:
"We decided= to keep the TOKEN at 6-9 words, not 12 or anything above precisely for thi= s reason. Please note that the user has to back up their Signers first, bef= ore proceeding to setting up the multisig wallet. So there's no writing= both things down at once or mixing of the two flows here.

I also fi= nd it hard to believe that someone who wants to invest in a safe multisig s= olution (and therefore must know at minimum what keys and multisig represen= t) will not know the difference between (permanent) 12 words and (one-time = use) 6 words. Also note that the TOKEN can be used without using BIP39 mnem= onic at all."

We also made the decimal format, not BIP39 mnemon= ic, the recommended encoding in the spec.

Best,
Hugo
=C2=A0
--0000000000000b20c905bfb51f60--