Open andrewkozlik opened 3 years ago
Another option is to change the signing algorithm to always produce signatures that are 70 bytes in length
wouldn't this make Trezor transactions (weakly) distinguishable?
Another option is to change the signing algorithm to always produce signatures that are 70 bytes in length
wouldn't this make Trezor transactions (weakly) distinguishable?
Correct, option 2. allows an analyst to determine that a transaction was not signed by Trezor when the length doesn't match. BTW there are several other things in addition to fees/weight and signature lengths that can be used for analysis: transaction version, locktime, sequence, input and output ordering, maybe UTXO selection. So making Trezor indistinguishable from other wallets may prove challenging.
all of those are supplied externally though (so you're distinguishing Trezor Suite, not the device)
Update: I forgot about the low-S rule, which means that signatures are between 70 and 71 bytes. I fixed the original post to account for this.
Electrum generates signatures of (at most) 70 bytes in length [1], but they assume the maximum of 71 bytes [2], because hardware wallets may generate 71-byte signatures. Apparently Bitcoin core also generates signatures of 70 bytes in length [3].
[1] https://github.com/spesmilo/electrum/blob/master/electrum/ecc.py#L473
I looked at two more wallets.
Sparrow Wallet generates signatures of 70 bytes in length: https://github.com/sparrowwallet/drongo/blob/083288061ffe6e08805bb58108a9afab0d93fb0f/src/main/java/com/sparrowwallet/drongo/crypto/ECKey.java#L353 and assumes 70 bytes for fee estimation: https://github.com/sparrowwallet/drongo/blob/083288061ffe6e08805bb58108a9afab0d93fb0f/src/main/java/com/sparrowwallet/drongo/protocol/TransactionSignature.java#L47-L56
Mycelium Wallet generates signatures of at most 71 bytes: https://github.com/mycelium-com/wallet-android/blob/aea49dc14314eec461ad307af7f8f99abe4ddf79/bitlib/src/main/java/com/mrd/bitlib/crypto/InMemoryPrivateKey.java#L353-L366 Mycelium's fee estimator is here: https://github.com/mycelium-com/wallet-android/blob/master/bitlib/src/main/java/com/mrd/bitlib/FeeEstimator.kt
Based on the statistics from https://transactionfee.info/charts/bitcoin-script-ecdsa-r-value/ it appears that currently 1/3 of transactions signatures are generated by wallets that enforce a low-r value.
FTR, an interesting blog about wallet fingerprinting https://ishaana.com/blog/wallet_fingerprinting/, where this is referred to as "low-r grinding".
The weight of the transaction produced by Trezor is currently indeterminate if non-Taproot inputs are present. The reason for this is that the DER encoding of the signature is between 70 and 71 bytes depending on the particular signature. There is no way to tell in advance what the length will be. (To be precise, there is a small chance that it will be less than 70, but never more than 71.) Taproot signatures are not a problem because they are always 64 bytes.
TxWeightCalculator
incore
currently assumes that the signature will be 71 bytes in length, i.e. the maximum. We should evaluate whether this is a good choice. One thing to take into consideration here is to make Trezor transactions indistinguishable from transactions produced by other wallets. If there is a consensus in the community about the assumed signature size, then we should adopt it in Suite and Trezor.Another option is to change the signing algorithm to always produce signatures that are 70 bytes in length, i.e. if the signature does not come out as 70 bytes, then retry. This would make the transaction weight deterministic and would save on transaction fees. The number of retries would be 1 on average, i.e. a total of 2 signing operations per signature.
Summary of options for DER signature length:
(Edited, because I previously forgot about the low-S rule.)