2021 02 27 dersignatureutil mv (#2730)

* Move CryptoParams to shared crypto project, make BouncyCastleCryptoParams for bouncy castle specific things

* Rework build structure to work better with scalajs

* Add unit test to make sure CryptoParams & BouncyCastleCryptoParams are consistent
This commit is contained in:
Chris Stewart 2021-02-28 07:09:22 -06:00 committed by GitHub
parent ecae07c8e1
commit 5ba7b553b0
18 changed files with 210 additions and 157 deletions

View File

@ -23,4 +23,4 @@ jobs:
~/.bitcoin-s/binaries
key: ${{ runner.os }}-cache
- name: run tests
run: sbt ++2.12.12 downloadBitcoind coverage chainTest/test chain/coverageReport chain/coverageAggregate chain/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls cryptoTest/test cryptoJVM/coverageReport cryptoJVM/coverageAggregate cryptoJVM/coveralls coreTest/test core/coverageReport core/coverageAggregate core/coveralls secp256k1jni/test zmq/test zmq/coverageReport zmq/coverageAggregate zmq/coveralls appCommonsTest/test appServerTest/test oracleServerTest/test
run: sbt ++2.12.12 downloadBitcoind coverage chainTest/test chain/coverageReport chain/coverageAggregate chain/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls cryptoJVM/test cryptoTest/test cryptoJVM/coverageReport cryptoJVM/coverageAggregate cryptoJVM/coveralls coreTest/test core/coverageReport core/coverageAggregate core/coveralls secp256k1jni/test zmq/test zmq/coverageReport zmq/coverageAggregate zmq/coveralls appCommonsTest/test appServerTest/test oracleServerTest/test

View File

@ -23,4 +23,4 @@ jobs:
~/.bitcoin-s/binaries
key: ${{ runner.os }}-cache
- name: run tests
run: sbt ++2.13.5 downloadBitcoind coverage chainTest/test chain/coverageReport chain/coverageAggregate chain/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls cryptoTest/test cryptoJVM/coverageReport cryptoJVM/coverageAggregate cryptoJVM/coveralls coreTest/test core/coverageReport core/coverageAggregate core/coveralls secp256k1jni/test zmq/test zmq/coverageReport zmq/coverageAggregate zmq/coveralls appCommonsTest/test appServerTest/test oracleServerTest/test
run: sbt ++2.13.5 downloadBitcoind coverage chainTest/test chain/coverageReport chain/coverageAggregate chain/coveralls nodeTest/test node/coverageReport node/coverageAggregate node/coveralls cryptoTest/test cryptoJVM/test cryptoJVM/coverageReport cryptoJVM/coverageAggregate cryptoJVM/coveralls coreTest/test core/coverageReport core/coverageAggregate core/coveralls secp256k1jni/test zmq/test zmq/coverageReport zmq/coverageAggregate zmq/coveralls appCommonsTest/test appServerTest/test oracleServerTest/test

View File

@ -42,7 +42,5 @@ buildInfoPackage := "org.bitcoins.docs"
Test / bloopGenerate := None
Compile / bloopGenerate := None
libraryDependencies ++= Deps.docs
//https://stackoverflow.com/questions/26940253/in-sbt-how-do-you-override-scalacoptions-for-console-in-all-configurations
scalacOptions in Compile ~= (_.filterNot(s => s == "-Xfatal-warnings"))

View File

@ -1,7 +1,3 @@
name := "bitcoin-s-bitcoind-rpc-test"
libraryDependencies ++= Deps.bitcoindRpcTest(scalaVersion.value)
Test / test := (Test / test dependsOn {
Projects.bitcoindRpc / TaskKeys.downloadBitcoind
}).value

View File

@ -46,16 +46,11 @@ lazy val crypto = crossProject(JVMPlatform, JSPlatform)
name := "bitcoin-s-crypto"
)
.jvmSettings(
libraryDependencies ++= Seq(
"org.scala-js" %% "scalajs-stubs" % "1.0.0" % "provided",
"org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
)
libraryDependencies ++= Deps.cryptoJVM
)
.jsSettings(commonJsSettings)
.settings(
libraryDependencies ++= Seq(
"org.scodec" %%% "scodec-bits" % V.scodecV withSources () withJavadoc ()
)
libraryDependencies ++= Deps.crypto.value
)
.in(file("crypto"))
@ -63,7 +58,10 @@ lazy val cryptoJS = crypto.js
lazy val cryptoJVM = crypto.jvm.dependsOn(secp256k1jni)
lazy val core = project in file("core") dependsOn cryptoJVM
lazy val core = project
.in(file("core"))
.settings(libraryDependencies ++= Deps.core.value)
.dependsOn(cryptoJVM)
lazy val bitcoindRpc = project
.in(file("bitcoind-rpc"))
@ -288,7 +286,8 @@ lazy val cryptoTest = project
.in(file("crypto-test"))
.settings(CommonSettings.testSettings: _*)
.settings(
name := "bitcoin-s-crypto-test"
name := "bitcoin-s-crypto-test",
libraryDependencies ++= Deps.cryptoTest.value
)
.dependsOn(
cryptoJVM % testAndCompile,
@ -299,7 +298,8 @@ lazy val coreTest = project
.in(file("core-test"))
.settings(CommonSettings.testSettings: _*)
.settings(
name := "bitcoin-s-core-test"
name := "bitcoin-s-core-test",
libraryDependencies ++= Deps.coreTest.value
)
.dependsOn(
core % testAndCompile,
@ -434,7 +434,7 @@ lazy val dbCommons = project
.settings(CommonSettings.prodSettings: _*)
.settings(
name := "bitcoin-s-db-commons",
libraryDependencies ++= Deps.dbCommons
libraryDependencies ++= Deps.dbCommons.value
)
.dependsOn(core, appCommons)
@ -451,7 +451,7 @@ lazy val feeProvider = project
.settings(CommonSettings.prodSettings: _*)
.settings(
name := "bitcoin-s-fee-provider",
libraryDependencies ++= Deps.feeProvider
libraryDependencies ++= Deps.feeProvider.value
)
.dependsOn(core, appCommons)
@ -460,14 +460,15 @@ lazy val feeProviderTest = project
.settings(CommonSettings.testSettings: _*)
.settings(
name := "bitcoin-s-fee-provider-test",
libraryDependencies ++= Deps.feeProviderTest
libraryDependencies ++= Deps.feeProviderTest.value
)
.dependsOn(core, core % testAndCompile, testkit)
lazy val zmq = project
.in(file("zmq"))
.settings(CommonSettings.prodSettings: _*)
.settings(name := "bitcoin-s-zmq", libraryDependencies ++= Deps.bitcoindZmq)
.settings(name := "bitcoin-s-zmq",
libraryDependencies ++= Deps.bitcoindZmq.value)
.dependsOn(
core % testAndCompile
)
@ -475,6 +476,8 @@ lazy val zmq = project
lazy val bitcoindRpcTest = project
.in(file("bitcoind-rpc-test"))
.settings(CommonSettings.testSettings: _*)
.settings(name := "bitcoin-s-bitcoind-rpc-test",
libraryDependencies ++= Deps.bitcoindRpcTest.value)
.dependsOn(core % testAndCompile, testkit)
lazy val bench = project
@ -491,7 +494,7 @@ lazy val eclairRpcTest = project
.in(file("eclair-rpc-test"))
.settings(CommonSettings.testSettings: _*)
.settings(
libraryDependencies ++= Deps.eclairRpcTest,
libraryDependencies ++= Deps.eclairRpcTest.value,
name := "bitcoin-s-eclair-rpc-test"
)
.dependsOn(core % testAndCompile, testkit)
@ -530,7 +533,7 @@ lazy val nodeTest =
// Scalatest issue:
// https://github.com/scalatest/scalatest/issues/556
Test / fork := false,
libraryDependencies ++= Deps.nodeTest
libraryDependencies ++= Deps.nodeTest.value
)
.dependsOn(
core % testAndCompile,
@ -543,7 +546,8 @@ lazy val testkit = project
.in(file("testkit"))
.settings(CommonSettings.prodSettings: _*)
.settings(
name := "bitcoin-s-testkit"
name := "bitcoin-s-testkit",
libraryDependencies ++= Deps.testkit.value
)
.dependsOn(
asyncUtils,
@ -561,6 +565,7 @@ lazy val testkit = project
lazy val docs = project
.in(file("bitcoin-s-docs")) // important: it must not be docs/
.settings(CommonSettings.testSettings: _*)
.settings(libraryDependencies ++= Deps.docs.value)
.dependsOn(
bitcoindRpc,
chain,

View File

@ -1,7 +1,3 @@
name := "bitcoin-s-core-test"
libraryDependencies ++= Deps.coreTest
publishArtifact := false
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck,

View File

@ -1,5 +1,3 @@
name := "bitcoin-s-core"
libraryDependencies ++= Deps.core
CommonSettings.prodSettings

View File

@ -1,7 +1,3 @@
name := "bitcoin-s-crypto-test"
libraryDependencies ++= Deps.cryptoTest
publishArtifact := false
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck,

View File

@ -10,7 +10,7 @@ class FieldElementTest extends BitcoinSUnitTest {
behavior of "FieldElement"
private val N = CryptoParams.curve.getN
private val N = CryptoParams.getN
it must "have serialization symmetry" in {
forAll(CryptoGenerators.fieldElement) { fe =>

View File

@ -0,0 +1,16 @@
package org.bitcoins.crypto
import org.bouncycastle.asn1.sec.SECNamedCurves
import org.bouncycastle.asn1.x9.X9ECParameters
import org.bouncycastle.crypto.params.ECDomainParameters
object BouncyCastleCryptoParams {
val params: X9ECParameters = SECNamedCurves.getByName("secp256k1")
/** The curve that bitcoin uses. */
val curve =
new ECDomainParameters(params.getCurve,
params.getG,
params.getN,
params.getH)
}

View File

@ -14,8 +14,8 @@ import scala.util.Try
object BouncyCastleUtil {
private val curve: ECCurve = CryptoParams.curve.getCurve
private val G: ECPoint = CryptoParams.curve.getG
private val curve: ECCurve = BouncyCastleCryptoParams.curve.getCurve
private val G: ECPoint = BouncyCastleCryptoParams.curve.getG
private def getBigInteger(bytes: ByteVector): BigInteger = {
new BigInteger(1, bytes.toArray)
@ -81,7 +81,7 @@ object BouncyCastleUtil {
new HMacDSAKCalculator(new SHA256Digest()))
val privKey: ECPrivateKeyParameters =
new ECPrivateKeyParameters(getBigInteger(privateKey.bytes),
CryptoParams.curve)
BouncyCastleCryptoParams.curve)
signer.init(true, privKey)
val components: Array[BigInteger] =
signer.generateSignature(dataToSign.toArray)
@ -114,7 +114,7 @@ object BouncyCastleUtil {
new HMacDSAKCalculatorWithEntropy(new SHA256Digest(), entropy))
val privKey: ECPrivateKeyParameters =
new ECPrivateKeyParameters(getBigInteger(privateKey.bytes),
CryptoParams.curve)
BouncyCastleCryptoParams.curve)
signer.init(true, privKey)
val components: Array[BigInteger] =
signer.generateSignature(dataToSign.toArray)
@ -138,7 +138,7 @@ object BouncyCastleUtil {
val resultTry = Try {
val publicKeyParams =
new ECPublicKeyParameters(decodePoint(publicKey.bytes),
CryptoParams.curve)
BouncyCastleCryptoParams.curve)
val signer = new ECDSASigner
signer.init(false, publicKeyParams)

View File

@ -27,7 +27,8 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
override def freshPrivateKey: ECPrivateKey = {
val generator: ECKeyPairGenerator = new ECKeyPairGenerator
val keyGenParams: ECKeyGenerationParameters =
new ECKeyGenerationParameters(CryptoParams.curve, secureRandom)
new ECKeyGenerationParameters(BouncyCastleCryptoParams.curve,
secureRandom)
generator.init(keyGenParams)
val keypair: AsymmetricCipherKeyPair = generator.generateKeyPair
val privParams: ECPrivateKeyParameters =
@ -63,7 +64,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
signature: ECDigitalSignature,
message: ByteVector): (ECPublicKey, ECPublicKey) = {
val curve = CryptoParams.curve
val curve = BouncyCastleCryptoParams.curve
val (r, s) = (signature.r.bigInteger, signature.s.bigInteger)
val m = new BigInteger(1, message.toArray)
@ -140,7 +141,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
BouncyCastleUtil.signWithEntropy(bytes, privateKey, entropy)
override def secKeyVerify(privateKeyBytes: ByteVector): Boolean =
CryptoParams.curve.getCurve
BouncyCastleCryptoParams.curve.getCurve
.isValidFieldElement(new BigInteger(1, privateKeyBytes.toArray))
override def verify(

View File

@ -1,32 +0,0 @@
package org.bitcoins.crypto
import org.bouncycastle.asn1.sec.SECNamedCurves
import org.bouncycastle.asn1.x9.X9ECParameters
import org.bouncycastle.crypto.params.ECDomainParameters
import java.math.BigInteger
/** Created by chris on 3/29/16.
* This trait represents all of the default parameters for our elliptic curve
*/
sealed abstract class CryptoParams {
/** This is the parameters for the elliptic curve bitcoin uses. */
def params: X9ECParameters = SECNamedCurves.getByName("secp256k1")
/** The curve that bitcoin uses. */
def curve =
new ECDomainParameters(params.getCurve,
params.getG,
params.getN,
params.getH)
/** This is used for canonicalising the S value of a digital signature.
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
* @return
*/
def halfCurveOrder: BigInteger = curve.getN.shiftRight(1)
}
object CryptoParams extends CryptoParams

View File

@ -29,7 +29,7 @@ class HMacDSAKCalculatorWithEntropy(digest: Digest, entropy: ByteVector)
private val K = new Array[Byte](hMac.getMacSize)
private val V = new Array[Byte](hMac.getMacSize)
private var n = CryptoParams.curve.getN
private var n = BouncyCastleCryptoParams.curve.getN
override def isDeterministic = true

View File

@ -0,0 +1,12 @@
package org.bitcoins.crypto
import org.scalatest.flatspec.AnyFlatSpec
class BouncyCastleCryptoParamsTest extends AnyFlatSpec {
behavior of "BouncyCastleCryptoParams"
it must "have the same CryptoParams.getN & BouncyCastleCryptoParams.getN" in {
assert(CryptoParams.getN == BouncyCastleCryptoParams.curve.getN)
}
}

View File

@ -0,0 +1,27 @@
package org.bitcoins.crypto
import scodec.bits.ByteVector
import java.math.BigInteger
/** Created by chris on 3/29/16.
* This trait represents all of the default parameters for our elliptic curve
*/
sealed abstract class CryptoParams {
/** Hex constant for curve group constant
*/
private val nConstant =
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
val getN: BigInteger =
new BigInteger(1, ByteVector.fromValidHex(nConstant).toArray)
/** This is used for canonicalising the S value of a digital signature.
* https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures
* @return
*/
val halfCurveOrder: BigInteger = getN.shiftRight(1)
}
object CryptoParams extends CryptoParams

View File

@ -192,9 +192,8 @@ sealed abstract class DERSignatureUtil {
val sigLowS =
if (isLowS(signature)) signature
else
ECDigitalSignature(
signature.r,
CryptoParams.curve.getN.subtract(signature.s.bigInteger))
ECDigitalSignature(signature.r,
CryptoParams.getN.subtract(signature.s.bigInteger))
require(DERSignatureUtil.isLowS(sigLowS))
sigLowS
}

View File

@ -1,4 +1,5 @@
import sbt._
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
object Deps {
@ -54,6 +55,7 @@ object Deps {
val sourcecodeV = "0.2.3"
val scalaJsStubsV = "1.0.0"
// CLI deps
val scoptV = "4.0.0"
val sttpV = "1.7.2"
@ -66,7 +68,8 @@ object Deps {
"org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
val scodec =
"org.scodec" %% "scodec-bits" % V.scodecV withSources () withJavadoc ()
Def.setting(
"org.scodec" %%% "scodec-bits" % V.scodecV withSources () withJavadoc ())
val slf4j =
"org.slf4j" % "slf4j-api" % V.slf4j % "provided" withSources () withJavadoc ()
@ -172,8 +175,12 @@ object Deps {
val scalacheck =
"org.scalacheck" %% "scalacheck" % V.scalacheck withSources () withJavadoc ()
val scalaJsStubs =
"org.scala-js" %% "scalajs-stubs" % V.scalaJsStubsV % "provided"
val scalaTest =
"org.scalatest" %% "scalatest" % V.scalaTest withSources () withJavadoc ()
Def.setting(
"org.scalatest" %%% "scalatest" % V.scalaTest withSources () withJavadoc ())
val scalaTestPlus =
"org.scalatestplus" %% "scalacheck-1-14" % V.scalaTestPlus withSources () withJavadoc ()
@ -195,7 +202,9 @@ object Deps {
val logback = Compile.logback % "test"
val grizzledSlf4j = Compile.grizzledSlf4j % "test"
val scalacheck = Compile.scalacheck % "test"
val scalaTest = Compile.scalaTest % "test"
val scalaTest = Def.setting(
"org.scalatest" %%% "scalatest" % V.scalaTest % "test" withSources () withJavadoc ())
val scalaMock = "org.scalamock" %% "scalamock" % V.scalamockV
val spray =
@ -236,43 +245,60 @@ object Deps {
Compile.slf4j
)
val core = List(
def core = Def.setting {
List(
Compile.bouncycastle,
Compile.scodec.value,
Compile.slf4j,
Compile.grizzledSlf4j
)
}
val cryptoJVM = List(
Compile.bouncycastle,
Compile.scodec,
Compile.slf4j,
Compile.grizzledSlf4j
Compile.scalaJsStubs
)
val crypto = List(
Compile.bouncycastle,
Compile.scodec
)
def crypto: Def.Initialize[Seq[ModuleID]] = {
Def.setting {
List(
Compile.scodec.value,
Test.scalaTest.value
)
}
}
val secp256k1jni = List(
Compile.nativeLoader,
Test.junitInterface
)
val coreTest = List(
Test.junitInterface,
Test.logback,
Test.scalaTest,
Test.spray,
Test.playJson,
Test.scalaCollectionCompat
)
def coreTest = Def.setting {
List(
Test.junitInterface,
Test.logback,
Test.scalaTest.value,
Test.spray,
Test.playJson,
Test.scalaCollectionCompat
)
}
val cryptoTest = List(
Test.scalaTest
)
def cryptoTest = Def.setting {
List(
Test.scalaTest.value
)
}
val bitcoindZmq = List(
Compile.zeromq,
Compile.slf4j,
Test.logback,
Test.scalacheck,
Test.scalaTest
)
def bitcoindZmq = Def.setting {
List(
Compile.zeromq,
Compile.slf4j,
Test.logback,
Test.scalacheck,
Test.scalaTest.value
)
}
val bitcoindRpc = List(
Compile.akkaHttp,
@ -280,34 +306,37 @@ object Deps {
Compile.typesafeConfig
)
def bitcoindRpcTest(scalaVersion: String) =
def bitcoindRpcTest = Def.setting {
List(
Test.akkaHttpTestkit,
Test.akkaStream,
Test.logback,
Test.scalaTest,
Test.scalaTest.value,
Test.scalacheck,
Test.newAsync,
Test.scalaCollectionCompat
)
}
val bench = List(
"org.slf4j" % "slf4j-api" % V.slf4j withSources () withJavadoc (),
Compile.logback
)
val dbCommons = List(
Compile.dropwizardMetrics,
Compile.flyway,
Compile.slick,
Compile.sourcecode,
Compile.logback,
Compile.sqlite,
Compile.postgres,
Compile.slickHikari,
Test.scalaTest,
Test.pgEmbedded
)
def dbCommons = Def.setting {
List(
Compile.dropwizardMetrics,
Compile.flyway,
Compile.slick,
Compile.sourcecode,
Compile.logback,
Compile.sqlite,
Compile.postgres,
Compile.slickHikari,
Test.scalaTest.value,
Test.pgEmbedded
)
}
def cli(scalaVersion: String) =
List(
@ -348,24 +377,30 @@ object Deps {
Compile.slf4j
)
val eclairRpcTest = List(
Test.akkaHttpTestkit,
Test.akkaStream,
Test.logback,
Test.scalaTest,
Test.scalacheck
)
def eclairRpcTest = Def.setting {
List(
Test.akkaHttpTestkit,
Test.akkaStream,
Test.logback,
Test.scalaTest.value,
Test.scalacheck
)
}
val feeProvider = List(
Compile.akkaHttp,
Compile.akkaActor,
Compile.akkaStream
)
def feeProvider = Def.setting {
List(
Compile.akkaHttp,
Compile.akkaActor,
Compile.akkaStream
)
}
val feeProviderTest = List(
Test.akkaTestkit,
Test.scalaTest
)
def feeProviderTest = Def.setting {
List(
Test.akkaTestkit,
Test.scalaTest.value
)
}
val node = List(
Compile.akkaActor,
@ -375,20 +410,24 @@ object Deps {
Compile.sqlite
)
val nodeTest = List(
Test.akkaTestkit,
Test.scalaTest,
Test.pgEmbedded
)
val nodeTest = Def.setting {
List(
Test.akkaTestkit,
Test.scalaTest.value,
Test.pgEmbedded
)
}
val testkit = List(
Compile.slf4j,
Compile.scalacheck,
Compile.scalaTest,
Compile.scalaTestPlus,
Compile.pgEmbedded,
Test.akkaTestkit
)
val testkit = Def.setting {
List(
Compile.slf4j,
Compile.scalacheck,
Compile.scalaTest.value,
Compile.scalaTestPlus,
Compile.pgEmbedded,
Test.akkaTestkit
)
}
def keyManager(scalaVersion: String) =
List(
@ -411,11 +450,13 @@ object Deps {
Test.pgEmbedded
)
val docs = List(
Compile.logback,
Test.scalaTest,
Test.logback
)
def docs = Def.setting {
List(
Compile.logback,
Test.scalaTest.value,
Test.logback
)
}
val walletServerTest = List(
Test.scalaMock,