Bitcoin multisig time locking challenges
Q: How do you prevent $5 wrench attacks?
A: Make funds impossible to move.
At Casa, we've thought through every conceivable form of loss and attack that a bitcoin holder might experience, including physical coercion. We've had several clients ask us about bolstering this with time locks. To date, we have NOT implemented time locks due to their complexities and challenges. This article is a deep dive into those issues.
As a reminder, our security model makes it exceptionally difficult to move funds by  protecting funds with a multi-signature, multi-device setup, and  geographically distributing the keys in that setup. With proper distance (requiring time and effort to travel to different locations), and proper access controls at the locations themselves, this ensures strong protections for our clients' coins. Nevertheless, keys could theoretically still be compromised, or a user could still potentially be coerced (with a $5 wrench attack / blackmail / ), into traveling around to gather a threshold of signatures to move funds.
In order to make it impossible for a user who is under duress to move funds, the coins need to be encumbered in such a way that the network will reject a signed transaction that spends them. The current way one could approach this is by using OP_CHECKTIMELOCKVERIFY to add additional spending conditions to certain public keys. For example, a script where one key can spend anytime, but the other can’t spend until after a certain time would look like this:
Or a 3-of-5 script where all 5 keys are time locked would look like this:
There are other time locking methods in the Bitcoin protocol, such as nLocktime and OP_CSV. However, they don't let you lock current UTXOs. Rather, they allow you to create locked pre-signed transactions that aren't valid until after a certain time. This functionality is useful for other constructions such as HTLCs, but not for ensuring that the current on-chain funds don't get spent. You can learn all about the different types of time locks in this post by James Prestwich:
Why not implement time locks today?
The additional security benefit is clear: funds become even “colder” because we know they can’t be spent, even by the key holder.
One problem with simply applying them to Casa accounts is that it breaks a fundamental aspect of our security model that results from our seedless setup - the flexibility to rotate keys for compromised devices. If all of the keys are time locked, then we can’t perform a transaction to sweep funds over to a new key set.
Could we only time lock a few keys? Sure, with a more complex script it’s technically possible. Imagine a script like so where two of the keys (4 & 5) are time locked:
OR we could envision a script where the available signing keys change in a multi-step manner, from 3-of-5 to 3-of-6 to 3-of-7.
OR you could go the other direction, where the number of signatures required decreases over time, from 3-of-5 to 2-of-5 to 1-of-5.
The problem with bitcoin script / OP_CLTV is that the lock is not applied to keys. It’s applied to entire branches of scripts. Thus, in order for you to have a script that can handle a variety of situations with what is effectively different time locks on different keys, the complexity and number of conditionals in the script will blow up rather quickly. This is bad for both the on-chain transaction fees and for the privacy of wallet users, as these scripts will be fairly unique and thus fingerprintable.
Gracefully degrading time locked multisig
If we wanted to create a locking script that follows the conditions where all 5 keys are locked until 1 year from now, but after that point we can sign with a lower threshold of keys every 6 months in case of compromised devices, we’d end up with a script like this:
Though we need to be especially careful about designing more complex scripts because we are effectively changing the security model! In each Casa multisig keyset, one is secured by Casa (for use in emergencies), and most keysets contain one Mobile Key (essentially a "hot" key). We should be especially careful of degrading to a single signature model and we’d want to avoid degrading to one where Casa could spend a client’s funds unilaterally via the Casa Recovery Key. We’d also probably want to avoid allowing the single key to be the client's Mobile Key.
Thus, we'd want a construction more like:
Costs imposed by degrading multisig
More complex scripts for degrading multisig may require a lot of additional logic in our application regarding which devices are allowed to spend from which UTXOs. For simplicity and sanity, it may be preferable to require ALL UTXOs in a wallet to be encumbered by the same lock time and same public keys.
As mentioned earlier, there are also privacy and transaction fee ramifications since every UTXO will now need to be protected by these more complex scripts, even if the time locking functionality never gets executed.
It will also make the guide for Sovereign Recovery (guides we provide our clients for recovering their funds using open source software, in case anything were to happen to Casa) far more complicated. It would no longer be possible for us to hand the user a single PDF with recovery data because the data would no longer be deterministic. This is because the redeem scripts would no longer be standard, but would instead contain dynamic data - the timelock values. So we’d either have to come up with a completely new (semi?) deterministic scheme for deciding timelock values or we’d have to export the redeem scripts to the user each time they created a new address. Most likely the latter. It’s also unclear (and doubtful) that any user-friendly software supports loading custom redeem scripts. This would further reduce the sovereignty of Casa users and increase their reliance upon our software.
The optimal solution
From the perspective of blockchain data / privacy / transaction fees, the costs for locking funds with more complex scripts will improve dramatically once Taproot is available to use on Mainnet. Taproot uses Merkelized Abstract Syntax Trees to partition the execution of complex Bitcoin scripts so that you only need to reveal the branches that get executed. In the "normal" case, you'll never even need to reveal any of the unused complex logic. On-chain it will just look like a single signature transaction was executed via the use of aggregated Schnorr signatures.
We could then lock funds in such a way that, under most conditions, the spend will only look like a single signature spend on the blockchain, even though it is actually a multi-signature scheme behind the scenes. But in the case where keys have been lost, we would conditionally execute and expose only the logical branches that are necessary. However, this still leaves the (Casa-less) wallet recovery issue due to the creation of complex, nonstandard nondeterministic redeem scripts.
To learn more about the issues arising from creating Bitcoin scripts that contain nondeterministic data, check out my keynote at the MIT Bitcoin Expo earlier this year:
As wallet developers begin to take advantage of more complex scripting with Taproot, I expect we'll need to develop new standards for how to describe the scripts that might be used to encumber funds in a given wallet. Then, hopefully we can devise templates that enable the recovery of complex wallets via a discovery process that is similar to how address derivation works today, simply with additional variables.
Losing sleep over your personal security?
Setting up a multisig Casa Gold account takes less than 10 minutes if you already have a hardware wallet. You can get started here!
Stay safe out there
Want more tips on Bitcoin security? Join the Casa Security Briefing for news + developments impacting your personal security and privacy.