From e9600e6ed88b632520ecd4e4fb27ffca13869469 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 22 Aug 2019 10:11:10 +1000 Subject: [PATCH 1/6] public keys aren't identical --- bip-schnorr.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bip-schnorr.mediawiki b/bip-schnorr.mediawiki index e96ee44a..b921b180 100644 --- a/bip-schnorr.mediawiki +++ b/bip-schnorr.mediawiki @@ -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 == From 01e1f6e6b238f1c859512704e4b9bf33fe21d1ec Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 22 Aug 2019 10:20:12 +1000 Subject: [PATCH 2/6] pk not p --- bip-schnorr.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bip-schnorr.mediawiki b/bip-schnorr.mediawiki index b921b180..4de25964 100644 --- a/bip-schnorr.mediawiki +++ b/bip-schnorr.mediawiki @@ -87,7 +87,7 @@ 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 === From 4643538d4f07193726c796a9fe354a39ba250dfb Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Thu, 22 Aug 2019 11:06:19 +1000 Subject: [PATCH 3/6] make secret key a 32-byte array called sk, introduce pubkey() --- bip-schnorr.mediawiki | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/bip-schnorr.mediawiki b/bip-schnorr.mediawiki index 4de25964..8e534031 100644 --- a/bip-schnorr.mediawiki +++ b/bip-schnorr.mediawiki @@ -117,13 +117,16 @@ 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 ''hashtag(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(p-1)/2 mod p'' ([https://en.wikipedia.org/wiki/Euler%27s_criterion Euler's criterion])For points ''P'' on the secp256k1 curve it holds that ''jacobi(y(P)) ≠ 0''.. +** 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)''. 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 +168,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(hashBIPSchnorrDerive(bytes(d) || m)) mod n''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 ''2256'' that this bias is not observable (''1 - n / 2256'' is around ''1.27 * 2-128'').. * Fail if ''k' = 0''. From d3951f63f3886b4942f624f11cc9082b4e6a1be0 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 23 Aug 2019 15:19:28 +1000 Subject: [PATCH 4/6] use p for taproot internal key --- bip-taproot.mediawiki | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bip-taproot.mediawiki b/bip-taproot.mediawiki index 769f13d8..ef30fee5 100644 --- a/bip-taproot.mediawiki +++ b/bip-taproot.mediawiki @@ -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'''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.'''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. * 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 ''kj+1 depend on whether ''kj < ej'' (lexicographically)'''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.: **** If ''kj < ej'': ''kj+1 = hashTapBranch(kj || ej)'''''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.. **** If ''kj ≥ ej'': ''kj+1 = hashTapBranch(ej || kj)''. -** Let ''t = hashTapTweak(bytes(P) || km) = hashTapTweak(c[1:33] || km)''. +** Let ''t = hashTapTweak(p || km)''. ** 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'''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., 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 === From fc74ec6b356c83ac380ba33e48a434c2e0d6a270 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 23 Aug 2019 15:42:00 +1000 Subject: [PATCH 5/6] key gen, verify, sign in intro --- bip-schnorr.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bip-schnorr.mediawiki b/bip-schnorr.mediawiki index 8e534031..9c80cd54 100644 --- a/bip-schnorr.mediawiki +++ b/bip-schnorr.mediawiki @@ -91,7 +91,7 @@ This proposal suggests to include the tag by prefixing the hashed data with ''SH === 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. From 30bc716add2e654847d59fbd49ea0f953589ab73 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Sat, 24 Aug 2019 15:08:05 +1000 Subject: [PATCH 6/6] note about pubkey collision --- bip-schnorr.mediawiki | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bip-schnorr.mediawiki b/bip-schnorr.mediawiki index 9c80cd54..bb2f9dcc 100644 --- a/bip-schnorr.mediawiki +++ b/bip-schnorr.mediawiki @@ -128,6 +128,8 @@ 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.