From 613f6ba4b9eab32930255dd355890c1b7a57f7ae Mon Sep 17 00:00:00 2001 From: Nadav Kohen Date: Wed, 16 Sep 2020 17:01:30 -0500 Subject: [PATCH] Updated Bouncy Castle Schnorr implementation to most recent BIP 340 (#2025) --- .../org/bitcoins/crypto}/CryptoUtilTest.scala | 10 ++--- .../bitcoins/crypto/BouncyCastleUtil.scala | 2 +- .../org/bitcoins/crypto/CryptoUtil.scala | 12 +++--- .../org/bitcoins/crypto/SchnorrNonce.scala | 42 ++----------------- 4 files changed, 15 insertions(+), 51 deletions(-) rename {core-test/src/test/scala/org/bitcoins/core/util => crypto-test/src/test/scala/org/bitcoins/crypto}/CryptoUtilTest.scala (94%) diff --git a/core-test/src/test/scala/org/bitcoins/core/util/CryptoUtilTest.scala b/crypto-test/src/test/scala/org/bitcoins/crypto/CryptoUtilTest.scala similarity index 94% rename from core-test/src/test/scala/org/bitcoins/core/util/CryptoUtilTest.scala rename to crypto-test/src/test/scala/org/bitcoins/crypto/CryptoUtilTest.scala index ca756230da..15cef78f2a 100644 --- a/core-test/src/test/scala/org/bitcoins/core/util/CryptoUtilTest.scala +++ b/crypto-test/src/test/scala/org/bitcoins/crypto/CryptoUtilTest.scala @@ -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")) } } diff --git a/crypto/src/main/scala/org/bitcoins/crypto/BouncyCastleUtil.scala b/crypto/src/main/scala/org/bitcoins/crypto/BouncyCastleUtil.scala index a100cb686c..43e687bea1 100644 --- a/crypto/src/main/scala/org/bitcoins/crypto/BouncyCastleUtil.scala +++ b/crypto/src/main/scala/org/bitcoins/crypto/BouncyCastleUtil.scala @@ -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 } } diff --git a/crypto/src/main/scala/org/bitcoins/crypto/CryptoUtil.scala b/crypto/src/main/scala/org/bitcoins/crypto/CryptoUtil.scala index 931ef65af6..b9cf608952 100644 --- a/crypto/src/main/scala/org/bitcoins/crypto/CryptoUtil.scala +++ b/crypto/src/main/scala/org/bitcoins/crypto/CryptoUtil.scala @@ -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" ) } diff --git a/crypto/src/main/scala/org/bitcoins/crypto/SchnorrNonce.scala b/crypto/src/main/scala/org/bitcoins/crypto/SchnorrNonce.scala index 5b9bc037cd..5d19693e52 100644 --- a/crypto/src/main/scala/org/bitcoins/crypto/SchnorrNonce.scala +++ b/crypto/src/main/scala/org/bitcoins/crypto/SchnorrNonce.scala @@ -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(