Updated Bouncy Castle Schnorr implementation to most recent BIP 340 (#2025)

This commit is contained in:
Nadav Kohen 2020-09-16 17:01:30 -05:00 committed by GitHub
parent 16599010c7
commit 613f6ba4b9
4 changed files with 15 additions and 51 deletions

View File

@ -1,6 +1,6 @@
package org.bitcoins.core.util
package org.bitcoins.crypto
import org.bitcoins.crypto.CryptoUtil
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator}
import org.bitcoins.testkit.util.BitcoinSUnitTest
import scodec.bits._
@ -97,13 +97,13 @@ class CryptoUtilTest extends BitcoinSUnitTest {
forAll(NumberGenerator.bytevector) { bytes =>
assert(
CryptoUtil.sha256SchnorrChallenge(bytes) == CryptoUtil
.taggedSha256(bytes, "BIP340/challenge"))
.taggedSha256(bytes, "BIP0340/challenge"))
assert(
CryptoUtil.sha256SchnorrNonce(bytes) == CryptoUtil
.taggedSha256(bytes, "BIP340/nonce"))
.taggedSha256(bytes, "BIP0340/nonce"))
assert(
CryptoUtil.sha256SchnorrAuxRand(bytes) == CryptoUtil
.taggedSha256(bytes, "BIP340/aux"))
.taggedSha256(bytes, "BIP0340/aux"))
}
}

View File

@ -189,7 +189,7 @@ object BouncyCastleUtil {
val computedR = challengePoint.add(sigPoint)
val yCoord = computedR.toPoint.getRawYCoord
yCoord != null && yCoord.sqrt() != null && computedR.schnorrNonce == rx
yCoord != null && !yCoord.testBitZero() && computedR.schnorrNonce == rx
case Failure(_) => false
}
}

View File

@ -43,11 +43,11 @@ trait CryptoUtil {
sha256(tagBytes ++ bytes)
}
// The tag "BIP340/challenge"
// The tag "BIP0340/challenge"
private val schnorrChallengeTagBytes = {
ByteVector
.fromValidHex(
"07e00dcd3055c1b36ee93effe4d7f266024cdef4116982ff5dfdc1a97e77062907e00dcd3055c1b36ee93effe4d7f266024cdef4116982ff5dfdc1a97e770629"
"7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c"
)
}
@ -55,11 +55,11 @@ trait CryptoUtil {
sha256(schnorrChallengeTagBytes ++ bytes)
}
// The tag "BIP340/nonce"
// The tag "BIP0340/nonce"
private val schnorrNonceTagBytes = {
ByteVector
.fromValidHex(
"a2ba14a6b39c1c505260bf3aceb07febde3ab34c35c9259d25bd6972f15e6564a2ba14a6b39c1c505260bf3aceb07febde3ab34c35c9259d25bd6972f15e6564"
"07497734a79bcb355b9b8c7d034f121cf434d73ef72dda19870061fb52bfeb2f07497734a79bcb355b9b8c7d034f121cf434d73ef72dda19870061fb52bfeb2f"
)
}
@ -67,11 +67,11 @@ trait CryptoUtil {
sha256(schnorrNonceTagBytes ++ bytes)
}
// The tag "BIP340/aux"
// The tag "BIP0340/aux"
private val schnorrAuxTagBytes = {
ByteVector
.fromValidHex(
"4b07426ad8630dcdbadf8dee1e94f09ac2df4e7ee2629e5e6b27c8666c8cf31e4b07426ad8630dcdbadf8dee1e94f09ac2df4e7ee2629e5e6b27c8666c8cf31e"
"f1ef4e5ec063cada6d94cafa9d987ea069265839ecc11f972d77a52ed8c1cc90f1ef4e5ec063cada6d94cafa9d987ea069265839ecc11f972d77a52ed8c1cc90"
)
}

View File

@ -2,35 +2,12 @@ package org.bitcoins.crypto
import scodec.bits.ByteVector
import scala.annotation.tailrec
import scala.util.Try
case class SchnorrNonce(bytes: ByteVector) extends NetworkElement {
require(bytes.length == 32, s"Schnorr nonce must be 32 bytes, get $bytes")
private val evenKey: ECPublicKey = ECPublicKey(s"02$hex")
private val oddKey: ECPublicKey = ECPublicKey(s"03$hex")
private val schnorrPublicKey: SchnorrPublicKey = new SchnorrPublicKey(bytes)
private val yCoordEven: Boolean = {
evenKey.toPoint.getRawYCoord.sqrt() != null
}
/** Computes the public key associated with a SchnorrNonce as specified in bip-schnorr.
* They y-coordinate is chosen to be a quadratic residue.
*/
val publicKey: ECPublicKey = {
if (yCoordEven) {
evenKey
} else {
oddKey
}
}
require(Try(publicKey).isSuccess,
s"Schnorr nonce must be a valid x coordinate, got $bytes")
require(
publicKey.toPoint.getRawYCoord.sqrt != null,
"Schnorr nonce must be an x coordinate for which a quadratic residue y coordinate exists")
val publicKey: ECPublicKey = schnorrPublicKey.publicKey
def xCoord: FieldElement = {
FieldElement(bytes)
@ -39,21 +16,8 @@ case class SchnorrNonce(bytes: ByteVector) extends NetworkElement {
object SchnorrNonce extends Factory[SchnorrNonce] {
@tailrec
def fromBytes(bytes: ByteVector): SchnorrNonce = {
if (bytes.length == 32) {
new SchnorrNonce(bytes)
} else if (bytes.length < 32) {
// means we need to pad the private key with 0 bytes so we have 32 bytes
SchnorrNonce.fromBytes(bytes.padLeft(32))
} else if (bytes.length == 33) {
// this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
SchnorrNonce.fromBytes(bytes.tail)
} else {
throw new IllegalArgumentException(
"Schnorr nonce cannot be greater than 33 bytes in size, got: " +
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
new SchnorrNonce(SchnorrPublicKey.fromBytes(bytes).bytes)
}
def kFromBipSchnorr(