-
Notifications
You must be signed in to change notification settings - Fork 7
Changes BIP-360 to use tapscript #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bip-0360.mediawiki
Outdated
<source> | ||
script witness stack = [pubkey, signature] # len(pubkey) > 520 bytes and len(signature) > 520 bytes | ||
tapscript = [OP_DUP, OP_HASH256, OP_PUSHDATA HASH256(expected_pubkey), OP_EQUALVERIFY, OP_CHECKSIG_SLH, OP_VERIFY] | ||
</source> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be OP_HASH256
or OP_SHA256
? If one over the other, why?
P2WSH uses OP_SHA256
, and I'm unsure about tapscript.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer HASH256 to avoid having to worry about length extension.
tapscript uses tagged hash which is SHA256 with a tag to do domain separation. We might want to use tagged hash for this.
HashWriter TaggedHash(const std::string& tag)
{
HashWriter writer{};
uint256 taghash;
CSHA256().Write((const unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
writer << taghash << taghash;
return writer;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer HASH256 to avoid having to worry about length extension.
If I understand right, I agree. It definitely should be 256 bit length.
My curiosity would be that P2QRH would be the first script to use HASH256 rather than SHA256.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand right, I agree. It definitely should be 256 bit length.
I'm talking about this https://en.wikipedia.org/wiki/Length_extension_attack It doesn't actually matter but it is nice not have to think about. It has been argued this was why P2SH used HASH160.
My curiosity would be that P2QRH would be the first script to use HASH256 rather than SHA256.
As written, it wouldn't be part of the P2QRH standard.
That said, I've been considering preventing OP_DUP from duplicating stack elements larger than 520 bytes. If that change was made then OP_CHECKSIG_SLH would be computing the hash and then it would become part of the standard. It might make sense to use SHA256 or tagged hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work so far! Just got a few changes and questions.
bip-0360.mediawiki
Outdated
of securely storing Bitcoins in a cold wallet for very long periods of time (50 to 100 years). | ||
|
||
For PQ signatures we considered the NIST approved SLH-DSA (SPHINCS+), ML-DSA (CRYSTALS-Dilithium), | ||
and FN-DSA (FALCON). Of these three algorithms, SLH-DSA has the largest signature size, but is the most conservative |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to mention our investigation into SQIsign here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good idea, I'll add something.
I attended a talk on SQIsign a few days ago. It appears rapid progress is being made on SQIsign's performance and code complexity. It is still too early to tell, but things are looking better for SQIsign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love to see some viable code for that so we can compare! I tried FastSQISignHD and the C was kind of a shitshow. All the key functions were commented out, and even after reenabling them, it revealed a bunch more work that needed to be done.
https://github.com/Pierrick-Dartois/SQISignHD-lib
This was a couple months ago, though, and it seems some more work has been done since.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SQIsign is definitely still baking but there is enormous amounts of research being done on it. Hard to evaluate right now and any evaluations are likely to change.
Co-authored-by: Hunter Beast <[email protected]>
|
||
== Specification == | ||
|
||
We define the signature scheme and transaction structure as follows. | ||
|
||
=== Descriptor Format === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering the rationale for removing this section on Descriptor Format ?
Seems like it would be useful when creating p2qrh compliant wallets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is being reworked and no longer fit the current plan. I want to finish the specification first and then revisit it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could also easily be done as a separate BIP as has been done for all the other output types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some small edits to the excellent recent SQIsign addition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed CI was failing. I fixed the bug I introduced.
, Ethan Heilman
also needs to be added to the README right next to Hunter Beast
.
Co-authored-by: Hunter Beast <[email protected]>
Co-authored-by: Hunter Beast <[email protected]>
|
||
== Specification == | ||
|
||
We define the signature scheme and transaction structure as follows. | ||
|
||
=== Descriptor Format === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could also easily be done as a separate BIP as has been done for all the other output types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This review is mostly concerned with formatting/grammar/etc, plus an observation regarding choosing only one lattice based signature type. I’ll do a more in depth review tomorrow of the technical contents.
FN-DSA (FALCON). Of these three algorithms, SLH-DSA has the largest signature size, but is the most conservative | ||
choice from a security perspective because SLH-DSA is based on well studied and time-tested hash-based cryptography. | ||
Both FN-DSA and ML-DSA signatures are significantly smaller than SLH-DSA signatures but are based on newer lattice-based | ||
cryptography. Since ML-DSA and FN-DSA are both similar lattice-based designs, we choose to only support one of them as the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it’s true that an advance in lattice based cryptanalysis might break both ML-DSA and FN-DSA, the constructions are sufficiently different that it is quite possible that one might be broken while the other remains intact. I’d argue for the inclusion of both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we choose to include both, I can make a PR against Ethan’s fork to fix the other instances where only ML-DSA is specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it’s true that an advance in lattice based cryptanalysis might break both ML-DSA and FN-DSA, the constructions are sufficiently different that it is quite possible that one might be broken while the other remains intact.
I should clarify this in the BIP, thanks
I’d argue for the inclusion of both.
Given the cost in complexity and maintenance of supporting multiple signature schemes, we want to support as few as we get away with. Even 2 PQ schemes is pushing it, but I'd argue for the "rule of two". As long as Bitcoin has two wildly different secure signature algorithms, the network will always be protected from the nightmare of having an unexpected break in one of the signature algorithms.
One of the main criticisms of BIP-360 prior to this draft is that it supports multiple PQ schemes.
That said, we are taking a modular approach here so avoid potential arguments avoid algorithms. BIP-360 is only for P2QRH and not the PQ signature opcodes. We include the PQ signature opcodes in the BIP because want to be sure P2QRH can support them, there is no requirement that if BIP-360 is activated we must activate ML-DSA instead of FN-DSA.
The following activation approaches are possible:
- Activate P2QRH with PQ signature algorithms (likely SHL-DSA, ML-DSA, but this could change)
- Activate P2QRH, SHL-DSA together and then later activate PQ signature algorithms
- Activate P2QRH by itself and then later activate PQ signature algorithms
My personal opinion is that at the time BIP-360 is has consensus among the community, we should look at the state of PQ cryptography and determine which of these activation approaches we wish to take. If ML-DSA has become the "first among equals" of PQ signature and if we are seeing ML-DSA HSMs hitting the market and being deployed in public clouds like AWS and GCP, (1) becomes very derisked. If lattices are under attack and there is an increasingly large ML-DSA doom and gloom maybe we do (2). If SQIsign variants are showing year over year performance improvements maybe we do (2) or (3) and take a wait and see approach. The idea should be to focus on a strong decision for P2QRH in parallel with work on the algorithms.
The proposal for SHL-DSA and ML-DSA opcodes here are not locking in or locking out any algorithms.
Thanks for your comment, I'm going to include a section saying what I just said in the BIP.
bip-0360.mediawiki
Outdated
1. The <code>scriptPubKey</code> must be of the form: | ||
We propose adding the opcodes OP_CHECKSIG_ML (ML-DSA) and OP_CHECKSIG_SLH (SLH-DSA) to tapscript by redefining OP_SUCCESSx | ||
opcodes. As ML-DSA and SLH-DSA are post-quantum signature algorithms this would allow the user of post-quantum signatures | ||
in tapscript. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following the relationship between BIP341 and BIP342, should we create a new BIP focused exclusively on our proposed extension to tapscript ? Seems we should define a new tapleaf version (0xC2 / 194) that is:
-
optional. p2qrh supports tap leaves with existing version 0xC0 (Schnorr or DER-encoded ecdsa)
-
extends tapscript (0xC0) with our proposed PQC op_codes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is the idea, although I don't want there to be two different versions of tapscript, so the opcodes will be added tapscript in general.
This also allows P2QRH to be fully independent of the PQ signature opcodes. You could activate the opcodes first or P2QRH first or both at the same time or some between them. If you activated the opcodes first you wouldn't be able to use them since you wouldn't be able to get the large 520 bytes signatures on the stack.
In theory you could have the witness stack element size increase be part of the PQ signature opcode softfork and not part of the P2QRH. This would you only get the witness stack element size increase if the redeem script has one of the PQ signature opcodes. I opted not to do this as it would add some complexity and all things being equal I tie break on the more simple solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added discussion on this in the rationale section
bip-0360.mediawiki
Outdated
[key_type]: 0x02 | ||
[pubkey_length]: 0x0701 (1793 bytes) | ||
[pubkey]: public_key_falcon_512 | ||
To spend a P2QRH output, the following conditions must be met: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose the following additional condition (inline with more comments below):
- Any tap scripts using PQC proposed in this BIP must reference the new tapleaf version corresponding to this BIP: 0xC2 (194 in digital representation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this in more detail? What is the benefit here? Are you proposing using tapleaf versioning for the opcodes rather than an OP_SUCCESSx softfork?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for hinting that all of this should be discussed in the context of redefining OP_SUCCESSx codes. The following two statements from your proposed changes clarify a lot:
A P2QRH script witness stack differs in only one way from a P2TR script witness stack. P2QRH does not limit the size of the witness stack elements to 520 bytes. This is needed because PQ signatures and public keys can be larger than 520 bytes.
P2QRH could be activated first and then ML-DSA and SLH-DSA could be independently activated by redefining OP_SUCCESSx opcodes.
So sounds like the initial p2qrh soft-fork will disable the stack element size, even though at that time, PQC might not initially be an option (and only Schnorr or ECDSA can initially be used). Is this correct ?
( I was originally thinking earlier that maybe a new future leaf version would be needed that complements the rollout of OP_SUCCESSx codes by defining the increased stack element size ).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining that, I get it now
So sounds like the initial p2qrh soft-fork will disable the stack element size, even though at that time, PQC might not initially be an option (and only Schnorr or ECDSA can initially be used). Is this correct ?
Yes, that is currently my thinking and the intent of what is written.
To be precise, we aren't disabling the stack element size, but raising it to 8,000 bytes by allowing the witness stack elements 8,000 bytes or less. This is limited increase as it would not allow pushing, creating or duplicating 8,000 byte elements in tapscript.
I very much wish PQ signatures were less than 520 bytes, but given the fact that they are greater than 520 bytes I don't see a better way to do it. Everything else introduces much more complexity. For instance:
-
You could have a second witness that only the PQ opcodes could access, but that seems like a special case that everyone will have to remember. Languages don't generally have "hidden memory" that programmers need to remember exists when they use select opcodes.
-
You could push proxy values on the stack which function like pointers to the PQ signatures. Again that feels like it breaks the assumptions that most tapscript developers have about the language and also adds lots of implementation complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect. Thank you. Makes sense.
the same 256-bit security level. One may ask why not use the existing output type P2WSH instead of add a new one? | ||
The problem with P2WSH is that it can only execute pre-tapscript scripts and not tapscript. | ||
New programs in Bitcoin ecosystem have largely moved to tapscript for new scripts. Using P2WSH would require turning | ||
back the clock and forcing projects to move from tapscript to pre-tapscript. More importantly, tapscript provides a far |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO merkle-tree-of-scripts is extremely important as well. Possibly more important in the QR context.
- On-chain-efficiency. Sets of scripts can be committed to without including every possible spending condition in a single script, which could potentially make for a very large script.
- More importantly, in a QR context where you might have multiple QR signature algorithms. If one of the algorithms is compromised, having a single P2WSH script would require you to reveal the vulnerable QR pubkey, whereas with P2QRH we can keep different algorithms in different leaves and only reveal the one we believe to be secure when spending. (I suppose in a P2WSH scheme you could require both QR signatures unconditionally, but that would be very heavy on chain)
In my mind the obvious immediate term P2QRH trees would would be: {pk(A), {ml_dsa(B), slh_dsa(C)}}
(a schnorr leaf which is cheapest to spend at depth 0, then at depth 1, a leaf for either quantum secure algorithm, forgive me if my pseudo-descriptor is a bit off)
EDIT: You do cover this later, my bad. It still would be good to mention something in this paragraph though I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reviews
-
I agree with you that keeping the Merkle tree from P2TR is important for those usecases. I'm concerned if I use this argument someone will dispute if those use cases are actually how people will use it. To avoid that argument I've tried to restrain my opinion here.
-
For ML-DSA we commit to the public key hashes in P2QRH tapleafs not the public key so they are the same size as as schnorr or SLH-DSA public keys. Still there is some sufficient savings using different branches for pubkeys rather than a pattern like:
IF 1:
checksigSchnorr
ELSE IF 2:
checksigML
ELSE IF 3:
checksigSLH
In my mind the obvious immediate term P2QRH trees would would be: {pk(A), {ml_dsa(B), slh_dsa(C)}}
Agreed
ML-DSA and SLH-DSA could be independently activated. If at some future point another signature | ||
algorithm was desired it could follow this pattern. | ||
|
||
We consider two different paths for activating PQ signatures in Bitcoin. The first approach is to redefine OP_SUCCESSx opcodes for each |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OP_CHECKSIG
, OP_CHECKSIGVERIFY
, and OP_CHECKSIGADD
all can support other signing algorithms without new opcodes. If the public key is not exactly 32 bytes long (x-only public key), then the signature check is considered to have succeeded. Of course there are other considerations, and I don't know if the proposed PQ schemes can be fit into these opcodes (especially given the stack size limit).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's interesting. I didn't know that
SLH-DSA has 32 byte public keys, ML-DSA has much larger public keys but we use a public key hash in the tapleaf so it is also 32 bytes. In the tapleaf world we would probably want to make the public keys 33-bytes with one byte to flag the type of algorithm. It's cool that we could softfork that in. I think we would still need the new tapleaf version for the increased initial stack element size if we were doing OP_SUCCESSx opcodes.
Number of signatures: | ||
Turning our attention to public keys larger than 520 bytes. This is not needed for SLH-DSA as its public key is only 32 bytes. | ||
This is a different problem than signatures as public keys are typically pushed onto | ||
the stack by the tapleaf script (redeem script) to commit to public keys in output. The OP_PUSHDATA opcode in tapscript fails if asked to push |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessary, or desirable. You're not constrained by the current PUSHDATA
opcode behavior except to whatever extent you want to mirror tapscript, this seems like a case where it makes sense to diverge from plain tapscript, and it shouldn't complicate implementation much at all (just need a flag for EvalScript()
that relaxes the pushdata limit). It'll also save you 32 witness bytes FWIW.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me qualify that a bit. you technically could redefine the push limits for PUSHDATA
in all P2QRH scripts, however, bip-341 recommends sharing leaf versions between witness versions for static analysis that may not have access to the scriptPubkey
(to disambiguate between witness versions). Therefore, I'd recommend raising the PUSHDATA
limits only in a new leaf version, which is needed anyway for the PQ schemes to raise the stack item size limit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My justification for this is purely on the grounds of simplicity and a good developer experience. I'd be willing to rethink if I get lots of push back from the community.
I want there to be only one tapscript regardless of output (P2QRH, P2TR). This allows script writers and tooling to not have to think about what output it will used in. This is also the reason I prefer to use OP_SUCCESSx rather than tapleaf.
Therefore, I'd recommend raising the PUSHDATA limits only in a new leaf version, which is needed anyway for the PQ schemes to raise the stack item size limit.
We do not need a new tapleaf version to raise the stack item size limit or redefine OP_DUP. Adding an new opcode via OP_SUCCESSx is sufficient unless I'm missing something.
I plan to move most the PQ details into the PQ signature BIP when that gets posted to github. Let's revisit this discussion when PQ signature BIP is in draft.
bip-0360.mediawiki
Outdated
|
||
For example, for CRYSTALS-Dilithium Level I, a single public key is 1,312 bytes, and a signature is 2,420 bytes, resulting in a substantial increase over current | ||
ECDSA or Schnorr signatures. | ||
For a Merkle path of length m, it would add an additional ~32 * m bytes to the P2QRH input<ref>If the number is large enough we will the compact size will need 2 bytes, increasing the size by 1 byte.</ref>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/we will//
It's probably worth being specific. If m > 7, then the compact size will be 2 bytes. (m = 7 means the control block will be 32 * 7 + 1 bytes = 225 bytes, m = 8 means the control block will be 32 * 8 + 1 bytes = 257 bytes)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the way you wrote it, used some of your words here. I don't want to get that into the weeds here.
We are both wrong here in that compact size does 1 byte, 3 bytes, 5 bytes.
https://bitcoin.stackexchange.com/a/110963/442
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha you're totally right, good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly minor nits. Overall looking very good!
I had another question about some of the BIP-32
verbiage that isn't part of this PR, so should I address that elsewhere?
bip-0360.mediawiki
Outdated
P2QRH (Pay to Quantum Resistant Hash) is a new output type that commits to the root of a tapleaf merkle tree. It is functionally | ||
the same as a P2TR (Pay to Taproot) output with the quantum vulnerable key-spend path removed. Since P2QRH has no key-spend path, P2QRH omits the | ||
taptweak public key as it is not needed. Instead a P2QRH output is just the 32 byte root of the tapleaf merkle tree as defined | ||
in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP-341]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP-341]. | |
in [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP-341], hashed as per below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
bip-0360.mediawiki
Outdated
@@ -79,8 +83,8 @@ As the value being sent increases, so too should the fee in order to commit the | |||
possible. Once the transaction is mined, it makes useless the public key revealed by spending a UTXO, so long as it is | |||
never reused. | |||
|
|||
It is proposed to implement a Pay to Quantum Resistant Hash (P2QRH) output type that relies on a PQC signature | |||
algorithm. This new output type protects transactions submitted to the mempool and helps preserve the free market by | |||
It is proposed to implement a Pay to Quantum Resistant Hash (P2QRH) output type that relies on a PQ signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: P2QRH does not rely on a PQ signature; the initial soft fork from this BIP simply removes the vulnerable taproot key spend path. Maybe something like
It is proposed to implement a Pay to Quantum Resistant Hash (P2QRH) output type that relies on a PQ signature | |
It is proposed to implement a Pay to Quantum Resistant Hash (P2QRH) output type that enables the use of a PQ signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find. Changed to: As the first step to address these issues we propose Pay to Quantum Resistant Hash (P2QRH), an output type that allows tapscript to be used in a quantum resistant manner.
bip-0360.mediawiki
Outdated
The problem with P2WSH is that it can only execute pre-tapscript scripts and not tapscript. | ||
New programs in Bitcoin ecosystem have largely moved to tapscript for new scripts. Using P2WSH would require turning | ||
back the clock and forcing projects to move from tapscript to pre-tapscript. More importantly, tapscript provides a far | ||
easier and safer upgrade path for adding PQ signatures. Changes to pre-script to enable it to support PQ signatures would likely |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly here, where you have both "pre-script" and "pre-tapscript". In both of these instances (here and the line below), maybe just Script
is sufficient, or the full pre-taproot Script
? With Script
capitalized to show we are talking about the specification, not any particular script.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pre-script is a typo, let fix that.
I prefer pre-tapscript to pre-taproot since what we care about here the scripting language not the output type.
Rewrote to try to clarify.
The problem with P2WSH is that it only works with pre-tapscript Script and cannot work with tapscript Script.
them to switch to sending their coins using the PQ signature algorithms. This allows the upgrade to quantum | ||
resistance to be largely invisible to users. | ||
|
||
Consider the P2QRH output with three tapscripts: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, having three tapscripts would not necessarily protect in the case of one of the three (Schnorr, ML-DSA, SLH-DSA) algorithms being broken. The broken algo could be used to spend the output, if the attacker had knowledge of the full script tree, and the public key itself (which would require a Grover walk in case of hashed keys). The only way to fully protect the output would be a single script that does something like
<schnorr key>
OP_CHECKSIG
<ml-dsa key>
OP_CHECKMLSIG_ADD
<slh-dsa key>
OP_CHECKSLSIG_ADD
3
OP_EQUAL
Of course, doing that would reveal the key to the broken algo, allowing a CRQC to attack it, but since we need 3/3 sigs that doesn't seem particularly problematic. But maybe we weren't trying to protect against the case where the script was revealed?
If the latter is the case, perhaps some explanatory text would suffice to remove the ambiguity. I had assumed on my first reading of BIP-360 that outputs need both ECC and PQ signatures, as per PXQDH and similar protocols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the helpful reviewing.
But maybe we weren't trying to protect against the case where the script was revealed?
The script template can be known to the attacker, but the actual public keys should not be known.
The assumption here is that the Schorr public key for OP_CHECKSIG is not known to the attacker. This is same assumption that lets us say P2PKH protects against long-exposure attacks. As long as you don't spend or otherwise reveal the Schorr public key you should be safe.
One could certainly do 3-of-3 with each of the algorithms, but the spending transaction would be huge.
Attempted to clarify in the BIP.
|
||
To spend a P2QRH output, the following conditions must be met: | ||
P2QRH uses SegWit version 3 outputs, resulting in addresses that start with <code>bc1r</code>, following |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's nice
Co-authored-by: Joey Yandle <[email protected]>
Co-authored-by: Joey Yandle <[email protected]>
Co-authored-by: Joey Yandle <[email protected]>
Yes, open an issue about what you want changed with the talk of BIP-32 |
Thanks for everyone who provided feedback over the weekend. It looks like everything has been addressed satisfactorily. Let's Get This Merged. Any further changes can take place in a new PR. |
* Rewrote rationale * Fix bolded principles * Actually fix bold * Updates to talk about signature + public key size rather than just signature size * Took a pass over rationale * Started work on specification * Adds example tapscript hybrid signatures * More work on the specification * Cleans up TODO * Fixing grammar, other minor changes * SHL --> SLH * Apply suggestions from code review Co-authored-by: Hunter Beast <[email protected]> * Adds discussion of SQIsign * Fixes broken llink to libbitcoinpqc Co-authored-by: Hunter Beast <[email protected]> * Fixes writing in SQIsign section Co-authored-by: Hunter Beast <[email protected]> * Add rational section on big signatures and public keys * Fixes typos * Adds script validation from BIP 341 * Add commas * Add design section, stack element size increase now in PQ sigs * Fixes typo * Fixes typos and formatting Co-authored-by: Hunter Beast <[email protected]> * Add authorship to readme * Add diagram of P2QRH merke tree, scriptPubKey and Witness * Remove completed todo * Add security section * Clean up wording, moves some things around * Minor rewording * Review suggestions Co-authored-by: Hunter Beast <[email protected]> * Clarified size differences * Changed header size and order * does --> doUpdate bip-0360.mediawiki Co-authored-by: Hunter Beast <[email protected]> * Add related work section * Better scale figure * Respond to review comments * remove double space Co-authored-by: Armin Sabouri <[email protected]> * Address review comments * Addressing Ademan comments * Sync source svg * Address review * Addresses review * Apply suggestions from code review Co-authored-by: Joey Yandle <[email protected]> * Update bip-0360.mediawiki Co-authored-by: Joey Yandle <[email protected]> * Update bip-0360.mediawiki Co-authored-by: Joey Yandle <[email protected]> * Addressing review comments * Addressing reviews --------- Co-authored-by: Hunter Beast <[email protected]> Co-authored-by: Armin Sabouri <[email protected]> Co-authored-by: Joey Yandle <[email protected]>
This is a draft of changes that move BIP-360 from using an witness attestation containing PQ signatures to instead using tapscript with a very P2TR like output format. The reason for this change was that much the witness attestation construction was intended to prevent PQ signatures and public keys from being as storage mechanisms, e.g. used to store JPEGs onchain by requiring that any PQ public key or PQ signature which was stored on-chain must successfully verify. Unfortunately research showed that requiring verification did not prevent using public keys and signatures for storage for details see: jpeg resistance of various post-quantum signature schemes, Westerbaan (2025). This PR abandons this earlier approach in favor of backwards compatibility with tapscript.
Changes:
TODOs:
Must be resolved before merging
assumes no stack element sizes greater than 520 bytes.
Open Questions
None
Closed Questions
Should P2QRH and PQ Signatures opcodes be separate BIPs: Yes
The case for:
The case against:
Sighash tag specific to signature algorithm? Yes
Currently BIP-360 proposes using the tag "TapSighash" in the tagged hash of the sighash for PQ signatures. I can see an argument that the sighash should be specific to the signature algorithm used to prevent signatures from one algorithm being used for an algorithm.
OP_CHECKSIG_VERIFY variants for PQ signatures? Yes (if we use OP_SUCCESSx opcodes)
Currently the specification only creates the OP_CHECKSIG for PQ signatures. Do want a OP_CHECKSIG_ML_VERIFY and OP_CHECKSIG_SHL_VERIFY?
I'm leaning toward no since users can get the same functionality with OP_VERIFY.
The yes argument is that script writers will expect OP_CHECKSIG_ML_VERIFY to exist.
OP_CHECKSIGADD variants for PQ signatures? Yes (if we use OP_SUCCESSx opcodes)
I'm leaning toward a strong yes:
New opcode OP_DUPHASH256? No
This approach would change the code of OP_DUP to prevent duplicating any stack element larger than 520 bytes. This would not be a consensus change as currently there is no way to put an object larger than 520 bytes on the stack.
Second, we would introduce an opcode OP_DUPHASH256 which duplicates a stack element of any size and then immediately hashes it. This allows a stack elements greater than 520 bytes to be compared to a hash without removing it from the stack. This functionality is needed to commit to PQ public keys in tapscript.
Not needed, approach of packaging pubkey and signature together and then building this into hash removes this requirement
Drop version byte in the control block? No
Currently the P2QRH control block consists of the version byte and the merkle path. The version byte is very similar to the header byte in a P2TR control block. The header byte has two purposes, specify the parity of the public key and providing versioning.
For:
Against:
Good to maintain tapleaf version compatibility especially because this could be used for PQ signature activation.
How to fix DUP + no witness stack element size attack? Simply limit OP_DUP to 520 bytes.
If we remove the witness stack element size limit for P2QRH and then someone runs a tapscript consisting of
DUP DUP DUP DUP ... DUP
they can amplify the memory usage but duplicating a very large stack element.This is limited to the maximum number of stack elements on the stack
BIP-342 Tapscript Resource Limits
This creates an amplification factor of 1,000.
Assume biggest value possible 3.999mb bytes. Such a transaction use 4,000mb and would be ~3,999,000+1,000 = 4mb bytes in size. ~1000x amplification
SLH-DSA signatures are 17,088 bytes. This means that a transaction spend that can push 17,088 bytes on the stack can at most use memory 17,088,000 (17mb). Such a transaction would be ~17,088+1,000 = 18 kb bytes in size. ~1000x amplification
Current tapscript allows would use 520kb for a transaction which is 520+1,000 = 1.5kb. 346x amplification
Thus, removing this limit is not catastrophic. It is only 3 times worse than the present day. Still, not great.
Solution - limit OP_DUP to stack elements 520 bytes and less
We can remove this threat entirely by limiting stack elements to 520 bytes or less