mirror of
https://github.com/bitcoin/bips.git
synced 2024-11-20 10:11:46 +01:00
Merge pull request #59 from ajtowns/201908-schnorr32-nits
32 byte pubkey nits
This commit is contained in:
commit
6653f9f883
@ -40,7 +40,7 @@ made:
|
||||
|
||||
[[File:bip-schnorr/speedup-batch.png|frame|This graph shows the ratio between the time it takes to verify ''n'' signatures individually and to verify a batch of ''n'' signatures. This ratio goes up logarithmically with the number of signatures, or in other words: the total time to verify ''n'' signatures grows with ''O(n / log n)''.]]
|
||||
|
||||
By reusing the same curve as Bitcoin has used for ECDSA, private and public keys remain identical for Schnorr signatures, and we avoid introducing new assumptions about elliptic curve group security.
|
||||
By reusing the same curve as Bitcoin has used for ECDSA, we are able to retain existing mechanisms for choosing private and public keys, and we avoid introducing new assumptions about elliptic curve group security.
|
||||
|
||||
== Description ==
|
||||
|
||||
@ -87,11 +87,11 @@ For example, without tagged hashing a bip-schnorr signature could also be valid
|
||||
|
||||
This proposal suggests to include the tag by prefixing the hashed data with ''SHA256(tag) || SHA256(tag)''. Because this is a 64-byte long context-specific constant, optimized implementations are possible (identical to SHA256 itself, but with a modified initial state). Using SHA256 of the tag name itself is reasonably simple and efficient for implementations that don't choose to use the optimization.
|
||||
|
||||
'''Final scheme''' As a result, our final scheme ends up using public key ''pk'' which is the X coordinate of a point ''P'' on the curve whose Y coordinate is a quadratic residue and signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' whose Y coordinate is a quadratic residue. The signature satisfies ''sG = R + tagged_hash(r || p || m)P''.
|
||||
'''Final scheme''' As a result, our final scheme ends up using public key ''pk'' which is the X coordinate of a point ''P'' on the curve whose Y coordinate is a quadratic residue and signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' whose Y coordinate is a quadratic residue. The signature satisfies ''sG = R + tagged_hash(r || pk || m)P''.
|
||||
|
||||
=== Specification ===
|
||||
|
||||
We first describe the verification algorithm, and then the signature algorithm.
|
||||
We first describe the key generation algorithm, then the verification algorithm, and then the signature algorithm.
|
||||
|
||||
The following convention is used, with constants as defined for secp256k1:
|
||||
* Lowercase variables represent integers or byte arrays.
|
||||
@ -117,13 +117,18 @@ The following convention is used, with constants as defined for secp256k1:
|
||||
** The function ''point(x)'', where ''x'' is a 32-byte array, returns the point ''P = lift_x(int(x))''.
|
||||
** The function ''hash<sub>tag</sub>(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''.
|
||||
** The function ''jacobi(x)'', where ''x'' is an integer, returns the [https://en.wikipedia.org/wiki/Jacobi_symbol Jacobi symbol] of ''x / p''. It is equal to ''x<sup>(p-1)/2</sup> mod p'' ([https://en.wikipedia.org/wiki/Euler%27s_criterion Euler's criterion])<ref>For points ''P'' on the secp256k1 curve it holds that ''jacobi(y(P)) ≠ 0''.</ref>.
|
||||
** The function ''pubkey(x)'', where ''x'' is a 32-byte array, returns ''bytes(dG)'' where ''d = int(x) mod n''.
|
||||
|
||||
=== Public Key Generation ===
|
||||
==== Public Key Generation ====
|
||||
|
||||
Input:
|
||||
* The secret key ''d'': an integer in the range ''1..n-1'' chosen uniformly at random.
|
||||
* The secret key ''sk'': a 32-byte array, generated uniformly at random
|
||||
|
||||
The public key corresponding to secret key ''d'' is ''bytes(dG)''.
|
||||
To generate the corresponding public key:
|
||||
* Fail if ''int(sk) = 0'' or ''int(sk) >= n''
|
||||
* The public key corresponding to secret key ''sk'' is ''pubkey(sk)''.
|
||||
|
||||
Note that the two secret keys ''sk'' and ''bytes(n-int(sk))'' will generate the same public key.
|
||||
|
||||
Alternatively, the public key can be created according to [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32] which describes the derivation of 33-byte compressed public keys.
|
||||
In order to translate such public keys into bip-schnorr compatible keys, the first byte must be dropped.
|
||||
@ -165,11 +170,13 @@ All provided signatures are valid with overwhelming probability if and only if t
|
||||
==== Signing ====
|
||||
|
||||
Input:
|
||||
* The secret key ''d' '': an integer in the range ''1..n-1''
|
||||
* The secret key ''sk'': a 32-byte array
|
||||
* The message ''m'': a 32-byte array
|
||||
|
||||
To sign ''m'' for public key ''bytes(dG)'':
|
||||
* Let ''P = dG''
|
||||
To sign ''m'' for public key ''pubkey(sk)'':
|
||||
* Let ''d' = int(sk)''
|
||||
* Fail if ''d' = 0'' or ''d' >= n''
|
||||
* Let ''P = d'G''
|
||||
* Let ''d = d' '' if ''jacobi(y(P)) = 1'', otherwise let ''d = n - d' ''.
|
||||
* Let ''k' = int(hash<sub>BIPSchnorrDerive</sub>(bytes(d) || m)) mod n''<ref>Note that in general, taking the output of a hash function modulo the curve order will produce an unacceptably biased result. However, for the secp256k1 curve, the order is sufficiently close to ''2<sup>256</sup>'' that this bias is not observable (''1 - n / 2<sup>256</sup>'' is around ''1.27 * 2<sup>-128</sup>'').</ref>.
|
||||
* Fail if ''k' = 0''.
|
||||
|
@ -64,7 +64,7 @@ The following rules only apply when such an output is being spent. Any other out
|
||||
* If there are at least two witness elements left, script path spending is used:
|
||||
** Call the second-to-last stack element ''s'', the script.
|
||||
** The last stack element is called the control block ''c'', and must have length ''33 + 32m'', for a value of ''m'' that is an integer between 0 and 32, inclusive. Fail if it does not have such a length.
|
||||
** Let ''P = point(c[1:33])'' where ''point'' is defined as in bip-schnorr. Fail if this point is not on the curve.
|
||||
** Let ''p = c[1:33]'' and let ''P = point(p)'' where ''point'' is defined as in bip-schnorr. Fail if this point is not on the curve.
|
||||
** Let ''l = c[0] & 0xfe'', the leaf version<ref>'''What is the purpose of the first byte of the control block?''' The first byte of the control block has three distinct functions:
|
||||
* The low bit is used to denote whether the ''Q'' point's Y coordinate is a quadratic residue.<ref>'''Why is the quadratic residuosity of the output public key's Y coordinate required in a script path spend?''' The ''point'' function always constructs a point with Y coordinate having that property, but because ''Q'' is constructed by adding the taproot tweak to the internal public key ''P'', it cannot easily be guaranteed that ''Q'' in fact has such a Y coordinate. We can not ignore the Y coordinate because it would prevent batch verification. Trying out multiple internal keys until there's such a ''Q'' is possible but undesirable and unnecessary since this information about the Y coordinate only consumes an unused bit.</ref>
|
||||
* By keeping the top two bits set to true, it can be guaranteed that scripts can be recognized without knowledge of the UTXO being spent, simplifying analysis. This is because such values cannot occur as first byte of the final stack element in either P2WPKH or P2WSH spends.
|
||||
@ -76,13 +76,13 @@ The following rules only apply when such an output is being spent. Any other out
|
||||
*** Let ''k<sub>j+1</sub> depend on whether ''k<sub>j</sub> < e<sub>j</sub>'' (lexicographically)<ref>'''Why are child elements sorted before hashing in the Merkle tree?''' By doing so, it is not necessary to reveal the left/right directions along with the hashes in revealed Merkle branches. This is possible because we do not actually care about the position of specific scripts in the tree; only that they are actually committed to.</ref>:
|
||||
**** If ''k<sub>j</sub> < e<sub>j</sub>'': ''k<sub>j+1</sub> = hash<sub>TapBranch</sub>(k<sub>j</sub> || e<sub>j</sub>)''<ref>'''Why not use a more efficient hash construction for inner Merkle nodes?''' The chosen construction does require two invocations of the SHA256 compression functions, one of which can be avoided in theory (see BIP98). However, it seems preferable to stick to constructions that can be implemented using standard cryptographic primitives, both for implementation simplicity and analyzability. If necessary, a significant part of the second compression function can be optimized out by [https://github.com/bitcoin/bitcoin/pull/13191 specialization] for 64-byte inputs.</ref>.
|
||||
**** If ''k<sub>j</sub> ≥ e<sub>j</sub>'': ''k<sub>j+1</sub> = hash<sub>TapBranch</sub>(e<sub>j</sub> || k<sub>j</sub>)''.
|
||||
** Let ''t = hash<sub>TapTweak</sub>(bytes(P) || k<sub>m</sub>) = hash<sub>TapTweak</sub>(c[1:33] || k<sub>m</sub>)''.
|
||||
** Let ''t = hash<sub>TapTweak</sub>(p || k<sub>m</sub>)''.
|
||||
** If ''t ≥ 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141'' (order of secp256k1), fail.
|
||||
** Let ''Q = point(q) if (c[0] & 1) = 1 and -point(q) otherwise''. Fail if this point is not on the curve.
|
||||
** If ''Q ≠ P + int(t)G'', fail.
|
||||
** Execute the script, according to the applicable script rules<ref>'''What are the applicable script rules in script path spends?''' Bip-tapscript specifies validity rules that apply if the leaf version is ''0xc0'', but future proposals can introduce rules for other leaf versions.</ref>, using the witness stack elements excluding the script ''s'', the control block ''c'', and the annex ''a'' if present, as initial stack.
|
||||
|
||||
''q'' is referred to as ''taproot output key'' and ''c[1:33]'' as ''taproot internal key''.
|
||||
''q'' is referred to as ''taproot output key'' and ''p'' as ''taproot internal key''.
|
||||
|
||||
=== Signature validation rules ===
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user