Anyone figured out how to utilize "OP_SHA256 OP_CHECKSIG" in a TapScript? Is it even possible to spend it?
From what I've gathered, it needs an input of "<signature> <message>". Then, the hash of that message might be used as the x-value for the public key. But after digging into CAT and Schnorr Tricks I, it looks like it could work if the "<message>" includes something like "HASH(G||G||txdata)".
Also, since any "<signature>" is just a mix of "<r,s>" values, it could potentially be a sum of "<r1+r2,s1+s2>". So, we might be able to generate separate signatures in advance and then combine them by tweaking some parameters. What are your thoughts? Any ideas on how to make this TapScript work?
Using "OP_SHA256 OP_CHECKSIG" in a TapScript
10 replies 168 views
ninja_nodeFull Member
Posts: 89 · Reputation: 647
#2May 26, 2022, 12:59 PM
I didn't expect OP_SHA256 OP_CHECKSIG will be mentioned in this context, but here it is: https://groups.google.com/g/bitcoindev/c/LpWOcXMcvk8
Hello,
Yes, this is the territory of Schnorr signature linearity. You're absolutely right that:
Signatures (r, s) are linear: s = k + e * x
You can split keys and nonces: x = x1 + x2, k = k1 + k2
You can generate partial signatures and then combine them: s = s1 + s2
This is exactly how MuSig works, and it's how threshold signatures, co-signing, and adaptor signatures are done.
But again to make this work in your OP_SHA256 OP_CHECKSIG script, you'd need:
A way to ensure that SHA256(msg) yields an x-coordinate of a known public key,
Best Regard,
Sally
DGCustomerFirst
There are signatures, where nobody knows the private key, and I am worried, that "OP_SHA256 OP_CHECKSIG" may be broken faster in that way, which could be used to trigger "quantum panic", even if there will be no such danger. For example: for public key equal to 032baf163f5e27261ab3228e61fb86dc98054abd514751fce93d7444e8fbc6a293, there is testnet3 transaction 3952b35bde53eb3f4871824f0b6b8c5ad25ca84ce83f04eb1c1d69b83ad6e448, with 09300602010102010103 as a valid signature, which uses SIGHASH_SINGLE bug.
In this case, it is a classical ECDSA signature, where public key recovery is possible, so it is easy, and by starting with some valid signature, and using fixed z-value of 0100000000000000000000000000000000000000000000000000000000000000, everyone can easily perform public key recovery, and create a connection between any (r,s) pair, and some pre-calculated public key.
In case of Schnorr signatures, doing the same thing is harder, because public key recovery is no longer possible. However, if the hashed message is e-value of something else, then it may be possible to make things tick, without using any quantum computer, and without knowing the private key in the first place. There may be some bugs, similar to SIGHASH_SINGLE, which could lead to making a false signal, where real keys in use will still be safe, even if someone will spend "OP_SHA256 OP_CHECKSIG" as a TapScript.
Edit: Another example: r-value and s-value are x-value and y-value of the public key, taken from the Genesis Block. Only Satoshi knows the private key, but coins from the public key 0214368623b6bab515c1f9218381e37ff7ae8dac54132bc7f2072dc55fa55db6c7 were successfully moved on testnet4 in 1c6aa1f6bb20409e0fa3b34e559b55aa05d6ac5506747455d23799cca539546c transaction, even though I am not Satoshi (because if I would know the private key, then I could move the coins sent to the Satoshi's mainnet address as well; or sign a valid signature on his behalf, and post it on forum).
ninja_nodeFull Member
Posts: 89 · Reputation: 647
#5May 28, 2022, 05:20 AM
You are right. Look at this transaction: https://mempool.space/testnet4/tx/fef38008747f7d9500c52b2f166d010f46f7f10ac5225ab9e7a2d80be2068086
Here, "z=r=s", and "k=d+1", so the signature can show us z-value directly, and the public key is just "Z-G", where "Z=lift_x(z)". Which means, that for many z-values, it is possible to apply public key recovery, and make a valid public key and signature, without knowing corresponding private key at all.
Does it mean, that ECDSA is broken? Of course not. But such tricks can raise panic in the future, so I guess we need a better quantum proof.
So far, it was done only with ECDSA, but I suspect similar tricks can be done with Schnorr signatures, if someone will find some weakness, and produce valid signature, without breaking any private keys.
huh this thread is all confused. there is no opcode that takes the message to be verified from the stack. CHECKSIG checks a signature of the (masked) transaction.
Can you explain where this question came from and why you thought there existed a operation that took the message hash from the stack?
ninja_nodeFull Member
Posts: 89 · Reputation: 647
#7May 29, 2022, 04:59 AM
It definitely is. People are just exploring different features, to implement, what they want, without any soft-forks, if possible. As ajtowns said: "Huge amounts of FAFO energy these days".
It is true, that there is no widely-implemented opcode, which would be easy to use and understand. However, it can be crafted with some math, without any forks. I expect there will be even more FAFO in the future, so if we are going to live in interesting times, then maybe we have to get used into that, and take some steps, to protect ourselves (if it is possible to be protected from such things).
I guess that's just one of the consequences of lifting next limits, one-by-one. Then, if there are less and less strict rules, and we have just FAFO, everyone can be a developer, and craft whatever it wants. Which means a lot of altcoin flood is going to hit Bitcoin in that way or another, since now they know, how to attach Proof of Work to any scripts, and execute any consensus on top of Bitcoin, which they would want. Which means also a lot of MEV in the future. And now, it may be only expanded further, if next features will be activated, or if people will find out, how to use math, to activate things anyway, without asking anyone for soft-fork deployments.
And that transaction can be controlled by repeatedly computing double SHA-256 for different transactions, until hitting some interesting results. And also, because of ECDSA calculations, it is possible to put them in a way, where things would work for any z-value.
Now, people can move coins from real public keys, with unknown private keys, by exploiting some bugs. Who knows, how much time it will take for people, to understand things better, and put more features on top of what we currently have, and discover things, which were always possible, since 2009.
First, stwenhao pointed out, that OP_SHA256 can be combined with OP_CHECKSIG in the context of TapScript, just because the size of the SHA-256 output is the same as the size of the public key, used inside TapScript. Then, exactly the same Script was mentioned by Tadge Dryja on the mailing list, as a way to prove, that quantum computers are there. Who knows, how the world would look like, if TapScript would use public keys prefixed with 02 or 03, or if legacy ECDSA would use x-only public keys.
As far as I know, this topic is one of the earliest examples, where "OP_SHA256 OP_CHECKSIG" is mentioned, but maybe someone else noticed it earlier than stwenhao, before December 2023. I guess the start of TapScript development is the earliest timestamp, where someone could think about it, because before TapScript, such things were simply not possible (or definitely not easily achievable, by consuming just two opcodes).
Sometimes, some features can be enabled only if you break something. If all public keys in secp256k1 will be broken, then it will have bigger consequences, than just making all P2PK outputs spendable. It will also allow enabling weird features on top of the current infrastructure, which if unblocked, can have serious consequences, like having actually working 256-bit calculator, enabled on every blockchain, which copy-pasted Bitcoin Script code.
Here and now, taking a message hash from the stack is difficult. Possible, but difficult. It requires a long Script, which will craft all of that manually, through a lot of weird opcodes, and pay large fees. However, by mining secp256k1 keys, it can be done easier. The more Proof of Work you use, the less sigops it costs, to verify any message inside Script. And after N operations, it can be profitable enough, to deploy things like that in practice, without any soft-forks.
Also, it is interesting, that quantum-resistant addresses could never be deployed, and we could still be safe, just by using timelocks, and enforcing some Proof of Work. Then, as long as nobody will break ECDSA and SHA-256 together, at the same time, we can be well-protected for decades.
Another source of weird ideas is this mailing list post: ColliderScript: Covenants in Bitcoin via 160-bit hash collisions. It simply means, that we have some cryptographic assumptions, which can keep things safe. But: if they are broken, then not only coins can be stolen, but they can be also protected. And then, a weird bug, like a hash collision, is turned into a feature, and widely used on a regular basis. In the same way, SHA-1 was turned into hardened SHA-1. If the same will happen with SHA-256 or ECDSA, I wouldn't be surprised: Lindy effect as its finest, I would say.
And yet another example, how people are trying to activate things, without soft-forks: Signing a Bitcoin Transaction with Lamport Signatures (no changes needed). As you can see, ossification is now perceived as "soft-forks are bad, no-forks are good". I guess we are going in that direction. I don't know, where it will bring us, but I know, that people should be ready for a lot of FAFO, and unexpected deployments of weird features, if that's our reality.
Nope.
paul.ninjaFull Member
Posts: 152 · Reputation: 539
#9May 29, 2022, 10:22 AM
Neat line of thought and there are a few Script and BIP340 gotchas buried in this:
The script behaves the following way -- In tapscript, OP_CHECKSIG verifies a 64-byte Schnorr signature (plus an optional sighash byte) against the transaction's BIP341 sighash and a 32-byte x-only pubkey you put on the stack. It does not take the message from the stack.
So a script like OP_SHA256 OP_CHECKSIG will hash whatever is on the top of the stack and then hand that 32-byte hash to OP_CHECKSIG as if it were a signature. That will always fail, because a valid Schnorr sig is 64 bytes. In other words, as written, this is not spendable.
If you wanted to also enforce a hash property you would need to preserve the original signature for OP_CHECKSIG .
For example:
This requires the witness to push a signature S such that SHA256(S) == H and also S verifies under the pubkey. But
making SHA256(S) hit an arbitrary target H is a 256-bit preimage problem. Even the signer who controls the nonce cannot feasibly grind that. Practically, this turns your coin into a time-locked tomb.
-- Linearity, CAT tricks, and "combine partial signatures"
Schnorr is linear, and off-chain you can do musig/adaptor/threshold tricks. Inside Bitcoin Script, you cannot recompose the BIP340 challenge e or stitch signatures because you do not have OP_CAT or OP_CHECKSIGFROMSTACK . OP_CHECKSIG always checks the canonical BIP341 message, not some stack-built hash. So the CAT/Schnorr-tricks papers you read do not map to this tapscript.
"SHA256(msg) equals an x-coordinate" and key recovery worries. BIP340's challenge is e = H(tag, R.x || P.x || m) . It is not SHA256(m). Forcing SHA256(m) to equal an x-coordinate does not give you a lever in OP_CHECKSIG , and it does not make key recovery easier or harder. If quantum breaks discrete log, both standard <P> OP_CHECKSIG and any OP_SHA256 decoration are equally toast. Adding OP_SHA256 does not add quantum risk or protection.
If your real goal is experimentation, then you might want to use Elements/Liquid where OP_CHECKSIGFROMSTACK is available. There you can actually check a signature against a stack-built message (e.g., a SHA256 you produced in-script).
Or perhaps, keeping it simple on Bitcoin: enforce any extra preimage constraints on data the spender provides, but keep the signature check untouched as <P> OP_CHECKSIG .
Note that you can push more than one element on the stack:
If you change SHA-256 implementation to something broken (for example, to a version, where only the first 16 out of 64 rounds are executed), then you can easily test, that your node will accept it, and you can make a valid transaction, if you can break the hash function behind OP_SHA256.
paul.ninjaFull Member
Posts: 152 · Reputation: 539
#11Jun 1, 2022, 03:41 PM
Thanks for the follow-up and you are right on one thing: OP_CHECKSIG in tapscript pops two items (sig, pubkey). But a couple clarifications matter:
What your script really checks, with witness [sig, m] and script:
the machine does:
-- hash m -> h
-- run OP_CHECKSIG(sig, h) treating h as the x-only pubkey
So the message on the stack is NOT the BIP341 sighash. It never becomes the message. It becomes the pubkey bytes. For the spend to succeed you must satisfy:
-- h = SHA256(m) = x(P) for some point P that is on secp256k1 and
-- sig is a valid BIP340 signature of the transaction under P
In other words: you must both know the private key for P and also know a SHA-256 preimage whose hash equals P.x. That second part is a pure 256-bit preimage problem.
About "breaking SHA-256 in the node" -- If you change your node to a 16-round toy SHA-256, you will only convince your own node. Mainnet/testnet consensus uses standard SHA-256. Your tx will be invalid to everyone else and get rejected. That is a local fork, not a proof you can spend such outputs on the real network.
If your goal is a real, on-Bitcoin test make the intent explicit and keep consensus intact, e.g.:
# require SHA256(m) == x(P) and then verify sig under P
OP_SHA256 <xP> OP_EQUALVERIFY <P> OP_CHECKSIG
Witness: [sig, m]. This output is spendable today only if you can find a preimage m for the chosen x(P). With real SHA-256 that is infeasible; with a weakened hash it becomes easy but only on a chain that agrees to your weakened hash, which Bitcoin does not.
Minor details to keep in mind:
BIP340 will fail if the 32-byte pubkey is not a valid x on curve (roughly half of 32-byte strings). Using x(P) avoids that.
The sighash used by OP_CHECKSIG is always the BIP341 message for the current tx/input; stack items never replace it.
Related topics
- Using Cold Electrum with Core in watch mode 11
- Bitcoin Core displaying coins (UTXOs) in a new tab 13
- Are you in favor of BIP-110? Let's get a Bitcoin poll going. 0
- Erlay seems to have some issues here’s a better proposal for a bitcoin protocol without invites 3
- New Optional Hourglass Implementation is Live 3
- Ways to earn some sats by contributing to bitcoin core development 5