mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 22:36:34 +01:00
Refactor crypto module to be compatible with Scala.js part 1 (#2719)
* Refactor crypto module to be compatible with Scala.js * more changes * some more changes * abstract out Schnorr stuff * abstract out adapter stuff * cleanup * some more cleanup * fix build * Removed references to ECPoint outside of .jvm scope * remove references to ECPoint from the shared code * cleanup * remove cirlular dependencies * more cleanup * cleanup * move SipHash to CryptoContext * scaladoc * scalafmt Co-authored-by: nkohen <nadavk25@gmail.com>
This commit is contained in:
parent
7aa68998f1
commit
c90f318fd7
44 changed files with 2305 additions and 648 deletions
|
@ -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 crypto/coverageReport crypto/coverageAggregate crypto/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 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
|
||||
|
|
|
@ -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 crypto/coverageReport crypto/coverageAggregate crypto/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/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
|
||||
|
|
44
build.sbt
44
build.sbt
|
@ -1,3 +1,4 @@
|
|||
import Deps.V
|
||||
import com.typesafe.sbt.SbtGit.GitKeys._
|
||||
|
||||
import scala.util.Properties
|
||||
|
@ -30,8 +31,39 @@ lazy val benchSettings: Seq[Def.SettingsDefinition] = {
|
|||
}
|
||||
|
||||
import Projects._
|
||||
lazy val crypto = project in file("crypto")
|
||||
lazy val core = project in file("core") dependsOn crypto
|
||||
|
||||
lazy val commonJsSettings = {
|
||||
Seq(
|
||||
scalaJSLinkerConfig ~= {
|
||||
_.withModuleKind(ModuleKind.CommonJSModule)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
lazy val crypto = crossProject(JVMPlatform, JSPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.settings(
|
||||
name := "bitcoin-s-crypto"
|
||||
)
|
||||
.jvmSettings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-js" %% "scalajs-stubs" % "1.0.0" % "provided",
|
||||
"org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
|
||||
)
|
||||
)
|
||||
.jsSettings(commonJsSettings)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scodec" %%% "scodec-bits" % V.scodecV withSources () withJavadoc ()
|
||||
)
|
||||
)
|
||||
.in(file("crypto"))
|
||||
|
||||
lazy val cryptoJS = crypto.js
|
||||
|
||||
lazy val cryptoJVM = crypto.jvm.dependsOn(secp256k1jni)
|
||||
|
||||
lazy val core = project in file("core") dependsOn cryptoJVM
|
||||
|
||||
lazy val bitcoindRpc = project
|
||||
.in(file("bitcoind-rpc"))
|
||||
|
@ -58,7 +90,8 @@ lazy val `bitcoin-s` = project
|
|||
cliTest,
|
||||
core,
|
||||
coreTest,
|
||||
crypto,
|
||||
cryptoJVM,
|
||||
cryptoJS,
|
||||
cryptoTest,
|
||||
dbCommons,
|
||||
dbCommonsTest,
|
||||
|
@ -97,7 +130,8 @@ lazy val `bitcoin-s` = project
|
|||
cliTest,
|
||||
core,
|
||||
coreTest,
|
||||
crypto,
|
||||
cryptoJVM,
|
||||
cryptoJS,
|
||||
cryptoTest,
|
||||
dbCommons,
|
||||
dbCommonsTest,
|
||||
|
@ -257,7 +291,7 @@ lazy val cryptoTest = project
|
|||
name := "bitcoin-s-crypto-test"
|
||||
)
|
||||
.dependsOn(
|
||||
crypto % testAndCompile,
|
||||
cryptoJVM % testAndCompile,
|
||||
testkit
|
||||
)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.gcs
|
|||
|
||||
import org.bitcoins.core.number.{UInt64, UInt8}
|
||||
import org.bitcoins.core.util.NumberUtil
|
||||
import org.bitcoins.crypto.SipHashKey
|
||||
import org.bitcoins.testkit.core.gen.NumberGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import org.scalacheck.Gen
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.core.gcs
|
|||
|
||||
import org.bitcoins.core.number.UInt64
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, SipHashKey}
|
||||
import org.bitcoins.testkit.core.gen.CryptoGenerators._
|
||||
import org.bitcoins.testkit.core.gen.NumberGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.bitcoins.core.crypto
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import org.bitcoins.core.hd.{BIP32Node, BIP32Path}
|
||||
import org.bitcoins.core.number.{UInt32, UInt8}
|
||||
import org.bitcoins.core.util._
|
||||
|
@ -202,15 +201,7 @@ sealed abstract class ExtPrivateKey
|
|||
val (il, ir) = hmac.splitAt(32)
|
||||
//should be ECGroup addition
|
||||
//parse256(IL) + kpar (mod n)
|
||||
val tweak = CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 =>
|
||||
val tweakByteArr =
|
||||
NativeSecp256k1.privKeyTweakAdd(il.toArray, key.bytes.toArray)
|
||||
ByteVector(tweakByteArr)
|
||||
case CryptoContext.BouncyCastle =>
|
||||
val sum = key.fieldElement.add(FieldElement(il))
|
||||
sum.bytes
|
||||
}
|
||||
val tweak = CryptoUtil.add(il, key)
|
||||
val childKey = ECPrivateKey(tweak)
|
||||
val fp = CryptoUtil.sha256Hash160(key.publicKey.bytes).bytes.take(4)
|
||||
ExtPrivateKey(version, depth + UInt8.one, fp, idx, ChainCode(ir), childKey)
|
||||
|
@ -476,16 +467,7 @@ sealed abstract class ExtPublicKey extends ExtKey {
|
|||
val hmac = CryptoUtil.hmac512(chainCode.bytes, data)
|
||||
val (il, ir) = hmac.splitAt(32)
|
||||
val priv = ECPrivateKey(il)
|
||||
val childPubKey = CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 =>
|
||||
val tweaked = NativeSecp256k1.pubKeyTweakAdd(key.bytes.toArray,
|
||||
il.toArray,
|
||||
priv.isCompressed)
|
||||
ECPublicKey(ByteVector(tweaked))
|
||||
case CryptoContext.BouncyCastle =>
|
||||
val tweak = ECPrivateKey.fromBytes(il).publicKey
|
||||
key.add(tweak)
|
||||
}
|
||||
val childPubKey = CryptoUtil.pubKeyTweakAdd(key, priv)
|
||||
|
||||
//we do not handle this case since it is impossible
|
||||
//In case parse256(IL) ≥ n or Ki is the point at infinity, the resulting key is invalid,
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.protocol.script.{EmptyScriptPubKey, ScriptPubKey}
|
|||
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
|
||||
import org.bitcoins.core.script.control.OP_RETURN
|
||||
import org.bitcoins.core.util.BytesUtil
|
||||
import org.bitcoins.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, SipHashKey}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
object BlockFilter {
|
||||
|
|
|
@ -2,8 +2,7 @@ package org.bitcoins.core.gcs
|
|||
|
||||
import org.bitcoins.core.number.{UInt64, UInt8}
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bouncycastle.crypto.macs.SipHash
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
import org.bitcoins.crypto.{CryptoUtil, SipHashKey}
|
||||
import scodec.bits.{BinStringSyntax, BitVector, ByteVector}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -49,21 +48,7 @@ object GCS {
|
|||
}
|
||||
|
||||
private def sipHash(item: ByteVector, key: SipHashKey): UInt64 = {
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki#hashing-data-objects
|
||||
val sipHashCParam = 2
|
||||
val sipHashDParam = 4
|
||||
|
||||
val sh = new SipHash(sipHashCParam, sipHashDParam)
|
||||
|
||||
val keyParam = new KeyParameter(key.bytes.toArray)
|
||||
|
||||
sh.init(keyParam)
|
||||
|
||||
val offset = 0
|
||||
|
||||
sh.update(item.toArray, offset, item.length.toInt)
|
||||
|
||||
val digest = sh.doFinal()
|
||||
val digest = CryptoUtil.sipHash(item, key)
|
||||
|
||||
UInt64.fromHex(digest.toHexString)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import org.bitcoins.crypto.{
|
|||
CryptoUtil,
|
||||
DoubleSha256Digest,
|
||||
DoubleSha256DigestBE,
|
||||
NetworkElement
|
||||
NetworkElement,
|
||||
SipHashKey
|
||||
}
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ class BIP340Test extends BitcoinSUnitTest {
|
|||
msg: ByteVector,
|
||||
expectedSig: SchnorrDigitalSignature): Assertion = {
|
||||
val secpSig = secKey.schnorrSign(msg, auxRand)
|
||||
val bouncyCastleSig = BouncyCastleUtil.schnorrSign(msg, secKey, auxRand)
|
||||
val bouncyCastleSig =
|
||||
BouncycastleCryptoRuntime.schnorrSign(msg, secKey, auxRand)
|
||||
assert(secpSig == expectedSig,
|
||||
s"Test $index failed signing for libsecp256k1")
|
||||
assert(bouncyCastleSig == expectedSig,
|
||||
|
@ -33,7 +34,8 @@ class BIP340Test extends BitcoinSUnitTest {
|
|||
expectedResult: Boolean,
|
||||
comment: String): Assertion = {
|
||||
val secpResult = pubKey.verify(msg, sig)
|
||||
val bouncyCastleResult = BouncyCastleUtil.schnorrVerify(msg, pubKey, sig)
|
||||
val bouncyCastleResult =
|
||||
BouncycastleCryptoRuntime.schnorrVerify(msg, pubKey, sig)
|
||||
assert(secpResult == expectedResult,
|
||||
s"Test $index failed verification for libsecp256k1: $comment")
|
||||
assert(bouncyCastleResult == expectedResult,
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import org.bitcoins.crypto.CryptoContext.{BouncyCastle, LibSecp256k1}
|
||||
import org.bitcoins.testkit.core.gen.{CryptoGenerators, NumberGenerator}
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import org.scalacheck.Gen
|
||||
import org.scalatest.{Outcome, Succeeded}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
||||
|
||||
|
@ -18,7 +15,7 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
override def withFixture(test: NoArgTest): Outcome = {
|
||||
CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 => super.withFixture(test)
|
||||
case CryptoContext.BouncyCastle =>
|
||||
case CryptoContext.BouncyCastle | CryptoContext.BCrypto =>
|
||||
logger.warn(s"Test ${test.name} skipped as Secp256k1 is not available.")
|
||||
Succeeded
|
||||
}
|
||||
|
@ -27,19 +24,19 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
it must "add private keys the same" in {
|
||||
forAll(CryptoGenerators.privateKey, CryptoGenerators.privateKey) {
|
||||
case (priv1, priv2) =>
|
||||
assert(priv1.addWithBouncyCastle(priv2) == priv1.addWithSecp(priv2))
|
||||
assert(
|
||||
BouncycastleCryptoRuntime
|
||||
.add(priv1, priv2) == LibSecp256k1CryptoRuntime.add(priv1, priv2))
|
||||
}
|
||||
}
|
||||
|
||||
it must "add public keys the same" in {
|
||||
forAll(CryptoGenerators.publicKey, CryptoGenerators.privateKey) {
|
||||
case (pubKey, privKey) =>
|
||||
val sumKeyBytes =
|
||||
NativeSecp256k1.pubKeyTweakAdd(pubKey.bytes.toArray,
|
||||
privKey.bytes.toArray,
|
||||
true)
|
||||
val sumKeyExpected = ECPublicKey.fromBytes(ByteVector(sumKeyBytes))
|
||||
val sumKey = pubKey.addWithBouncyCastle(privKey.publicKey)
|
||||
val sumKeyExpected =
|
||||
LibSecp256k1CryptoRuntime.pubKeyTweakAdd(pubKey, privKey)
|
||||
val sumKey =
|
||||
BouncycastleCryptoRuntime.pubKeyTweakAdd(pubKey, privKey)
|
||||
|
||||
assert(sumKey == sumKeyExpected)
|
||||
}
|
||||
|
@ -49,18 +46,19 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
forAll(CryptoGenerators.publicKey, CryptoGenerators.fieldElement) {
|
||||
case (pubKey, tweak) =>
|
||||
assert(
|
||||
pubKey.tweakMultiplyWithSecp(tweak) == pubKey
|
||||
.tweakMultiplyWithBouncyCastle(tweak))
|
||||
LibSecp256k1CryptoRuntime.tweakMultiply(pubKey, tweak) ==
|
||||
BouncycastleCryptoRuntime.tweakMultiply(pubKey, tweak))
|
||||
}
|
||||
}
|
||||
|
||||
it must "validate keys the same" in {
|
||||
val keyOrGarbageGen = Gen.oneOf(CryptoGenerators.publicKey.map(_.bytes),
|
||||
NumberGenerator.bytevector(33))
|
||||
val keyOrGarbageGen =
|
||||
Gen.oneOf(CryptoGenerators.publicKey.map(_.bytes),
|
||||
NumberGenerator.bytevector(33))
|
||||
forAll(keyOrGarbageGen) { bytes =>
|
||||
assert(
|
||||
ECPublicKey.isFullyValid(bytes, context = BouncyCastle) ==
|
||||
ECPublicKey.isFullyValid(bytes, context = LibSecp256k1)
|
||||
LibSecp256k1CryptoRuntime.isValidPubKey(bytes) ==
|
||||
BouncycastleCryptoRuntime.isValidPubKey(bytes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -68,16 +66,18 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
it must "decompress keys the same" in {
|
||||
forAll(CryptoGenerators.publicKey) { pubKey =>
|
||||
assert(
|
||||
pubKey.decompressed(context = BouncyCastle) == pubKey.decompressed(
|
||||
context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.decompressed(pubKey) ==
|
||||
BouncycastleCryptoRuntime.decompressed(pubKey)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it must "compute public keys the same" in {
|
||||
forAll(CryptoGenerators.privateKey) { privKey =>
|
||||
assert(
|
||||
privKey.publicKey(context = BouncyCastle) == privKey.publicKey(
|
||||
context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.publicKey(privKey) ==
|
||||
BouncycastleCryptoRuntime.publicKey(privKey)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,8 +85,10 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
forAll(CryptoGenerators.privateKey, NumberGenerator.bytevector(32)) {
|
||||
case (privKey, bytes) =>
|
||||
assert(
|
||||
privKey.sign(bytes, context = BouncyCastle) == privKey
|
||||
.sign(bytes, context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.sign(
|
||||
privKey,
|
||||
bytes) == BouncycastleCryptoRuntime.sign(privKey, bytes)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,10 +97,13 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
NumberGenerator.bytevector(32),
|
||||
NumberGenerator.bytevector(32)) { case (privKey, bytes, entropy) =>
|
||||
assert(
|
||||
privKey.signWithEntropy(bytes,
|
||||
entropy,
|
||||
context = BouncyCastle) == privKey
|
||||
.signWithEntropy(bytes, entropy, context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.signWithEntropy(
|
||||
privKey,
|
||||
bytes,
|
||||
entropy) == BouncycastleCryptoRuntime.signWithEntropy(privKey,
|
||||
bytes,
|
||||
entropy)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,11 +114,17 @@ class BouncyCastleSecp256k1Test extends BitcoinSUnitTest {
|
|||
val sig = privKey.sign(bytes)
|
||||
val pubKey = privKey.publicKey
|
||||
assert(
|
||||
pubKey.verify(bytes, sig, context = BouncyCastle) == pubKey
|
||||
.verify(bytes, sig, context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.verify(
|
||||
pubKey,
|
||||
bytes,
|
||||
sig) == BouncycastleCryptoRuntime.verify(pubKey, bytes, sig)
|
||||
)
|
||||
assert(
|
||||
pubKey.verify(bytes, badSig, context = BouncyCastle) == pubKey
|
||||
.verify(bytes, badSig, context = LibSecp256k1))
|
||||
LibSecp256k1CryptoRuntime.verify(
|
||||
pubKey,
|
||||
bytes,
|
||||
badSig) == BouncycastleCryptoRuntime.verify(pubKey, bytes, badSig)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class ECDigitalSignatureTest extends BitcoinSUnitTest {
|
|||
it must "be able to generate valid signatures with bouncy castle" in {
|
||||
forAll(CryptoGenerators.privateKey, CryptoGenerators.sha256Digest) {
|
||||
case (privKey: ECPrivateKey, hash: Sha256Digest) =>
|
||||
val sig = privKey.signWithBouncyCastle(hash.bytes)
|
||||
val sig = BouncycastleCryptoRuntime.sign(privKey, hash.bytes)
|
||||
val pubKey = privKey.publicKey
|
||||
|
||||
assert(pubKey.verify(hash, sig))
|
||||
|
|
|
@ -31,10 +31,14 @@ class ECPublicKeyTest extends BitcoinSUnitTest {
|
|||
}
|
||||
|
||||
it must "have serialization symmetry from ECPublicKey -> ECPoint -> ECPublicKey" in {
|
||||
forAll(CryptoGenerators.publicKey) { pubKey =>
|
||||
val p = pubKey.toPoint
|
||||
val pub2 = ECPublicKey.fromPoint(p, pubKey.isCompressed)
|
||||
assert(pubKey == pub2)
|
||||
CryptoContext.cryptoRuntime match {
|
||||
case _: BouncycastleCryptoRuntime | _: LibSecp256k1CryptoRuntime =>
|
||||
forAll(CryptoGenerators.publicKey) { pubKey =>
|
||||
val p = BouncyCastleUtil.decodePoint(pubKey)
|
||||
val pub2 = BouncyCastleUtil.decodePubKey(p, pubKey.isCompressed)
|
||||
assert(pubKey == pub2)
|
||||
}
|
||||
case _ => succeed
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** This is an implementation of [[CryptoRuntime]] that defaults to
|
||||
* Bcrypto (https://github.com/bcoin-org/bcrypto) when possible.
|
||||
*/
|
||||
trait BCryptoCryptoRuntime extends CryptoRuntime {
|
||||
override val cryptoContext: CryptoContext = CryptoContext.BCrypto
|
||||
|
||||
/** Generates a 32 byte private key */
|
||||
override def freshPrivateKey: ECPrivateKey = ???
|
||||
|
||||
/** Converts a private key -> public key
|
||||
*
|
||||
* @param privateKey the private key we want the corresponding public key for
|
||||
* @param isCompressed whether the returned public key should be compressed or not
|
||||
*/
|
||||
override def toPublicKey(
|
||||
privateKey: ECPrivateKey,
|
||||
isCompressed: Boolean): ECPublicKey = ???
|
||||
|
||||
override def ripeMd160(bytes: ByteVector): RipeMd160Digest = ???
|
||||
|
||||
override def sha256Hash160(bytes: ByteVector): Sha256Hash160Digest = ???
|
||||
|
||||
override def sha256(bytes: ByteVector): Sha256Digest = ???
|
||||
|
||||
override def sha1(bytes: ByteVector): Sha1Digest = ???
|
||||
|
||||
override def hmac512(key: ByteVector, data: ByteVector): ByteVector = ???
|
||||
|
||||
override def normalize(str: String): String = ???
|
||||
|
||||
/** Recover public keys from a signature and the message that was signed. This method will return 2 public keys, and the signature
|
||||
* can be verified with both, but only one of them matches that private key that was used to generate the signature.
|
||||
*
|
||||
* @param signature signature
|
||||
* @param message message that was signed
|
||||
* @return a (pub1, pub2) tuple where pub1 and pub2 are candidates public keys. If you have the recovery id then use
|
||||
* pub1 if the recovery id is even and pub2 if it is odd
|
||||
*/
|
||||
override def recoverPublicKey(
|
||||
signature: ECDigitalSignature,
|
||||
message: ByteVector): (ECPublicKey, ECPublicKey) = ???
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = ???
|
||||
|
||||
override def sign(
|
||||
privateKey: ECPrivateKey,
|
||||
dataToSign: ByteVector): ECDigitalSignature = ???
|
||||
|
||||
override def signWithEntropy(
|
||||
privateKey: ECPrivateKey,
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = ???
|
||||
|
||||
override def secKeyVerify(privateKeybytes: ByteVector): Boolean = ???
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = ???
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey = ???
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey = ???
|
||||
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey = ???
|
||||
|
||||
override def add(bytes: ByteVector, pk2: ECPrivateKey): ByteVector = ???
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = ???
|
||||
|
||||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = ???
|
||||
|
||||
override def isValidPubKey(bytes: ByteVector): Boolean = ???
|
||||
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean = ???
|
||||
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = ???
|
||||
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = ???
|
||||
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = ???
|
||||
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = ???
|
||||
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = ???
|
||||
|
||||
override def adaptorComplete(
|
||||
key: ECPrivateKey,
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = ???
|
||||
|
||||
override def extractAdaptorSecret(
|
||||
signature: ECDigitalSignature,
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey): ECPrivateKey = ???
|
||||
|
||||
override def adaptorVerify(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean = ???
|
||||
|
||||
override def decodeSignature(
|
||||
signature: ECDigitalSignature): (scala.BigInt, scala.BigInt) = ???
|
||||
|
||||
override def isValidSignatureEncoding(
|
||||
signature: ECDigitalSignature): Boolean = ???
|
||||
|
||||
override def isDEREncoded(signature: ECDigitalSignature): Boolean = ???
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long = ???
|
||||
}
|
||||
|
||||
object BCryptoCryptoRuntime extends BCryptoCryptoRuntime
|
1028
crypto/.js/src/main/scala/org/bitcoins/crypto/Buffer.scala
Normal file
1028
crypto/.js/src/main/scala/org/bitcoins/crypto/Buffer.scala
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
sealed protected trait CryptoRuntimeFactory {
|
||||
def newCryptoRuntime: CryptoRuntime = BCryptoCryptoRuntime
|
||||
}
|
||||
|
||||
protected object CryptoRuntimeFactory extends CryptoRuntimeFactory
|
19
crypto/.js/src/main/scala/org/bitcoins/crypto/ECDSA.scala
Normal file
19
crypto/.js/src/main/scala/org/bitcoins/crypto/ECDSA.scala
Normal file
|
@ -0,0 +1,19 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation._
|
||||
|
||||
/** Scala wrapper for
|
||||
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/ecdsa.js
|
||||
*/
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/js/ecdsa.js", JSImport.Default)
|
||||
class ECDSA(
|
||||
name: String = "SECP256K1",
|
||||
hash: SHA256 = new SHA256,
|
||||
xof: SHA256 = new SHA256,
|
||||
pre: String = null)
|
||||
extends js.Object {
|
||||
|
||||
def publicKeyCreate(key: Buffer, compressed: Boolean): Buffer = js.native
|
||||
}
|
17
crypto/.js/src/main/scala/org/bitcoins/crypto/Hash160.scala
Normal file
17
crypto/.js/src/main/scala/org/bitcoins/crypto/Hash160.scala
Normal file
|
@ -0,0 +1,17 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation._
|
||||
|
||||
/** The scala js wrapper for
|
||||
* https://github.com/bcoin-org/bcrypto/blob/master/lib/js/hash160.js
|
||||
*/
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/js/hash160.js", JSImport.Default)
|
||||
class Hash160() extends Hasher {
|
||||
override def init(): Unit = js.native
|
||||
|
||||
override def update(bytes: Buffer): Unit = js.native
|
||||
|
||||
override def `final`(): Buffer = js.native
|
||||
}
|
12
crypto/.js/src/main/scala/org/bitcoins/crypto/Hasher.scala
Normal file
12
crypto/.js/src/main/scala/org/bitcoins/crypto/Hasher.scala
Normal file
|
@ -0,0 +1,12 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
|
||||
/** All hash classes in bcrypto have this structure */
|
||||
trait Hasher extends js.Object {
|
||||
def init(): Unit
|
||||
|
||||
def update(bytes: Buffer): Unit
|
||||
|
||||
def `final`(): Buffer
|
||||
}
|
16
crypto/.js/src/main/scala/org/bitcoins/crypto/Random.scala
Normal file
16
crypto/.js/src/main/scala/org/bitcoins/crypto/Random.scala
Normal file
|
@ -0,0 +1,16 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation._
|
||||
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/random.js", JSImport.Namespace)
|
||||
object Random extends js.Object {
|
||||
def randomBytes(n: Int): Buffer = js.native
|
||||
}
|
||||
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/random-browser.js", JSImport.Namespace)
|
||||
object RandomBrowser extends js.Object {
|
||||
def randomBytes(n: Int): Buffer = js.native
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation._
|
||||
|
||||
//the annotation specifies that Foobaz is a native JS class defined in the module "bar.js", and exported under the name "Foo".
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/js/ripemd160.js", JSImport.Default)
|
||||
class RipeMd160() extends Hasher {
|
||||
|
||||
override def init(): Unit = js.native
|
||||
|
||||
override def update(bytes: Buffer): Unit = js.native
|
||||
|
||||
override def `final`(): Buffer = js.native
|
||||
}
|
17
crypto/.js/src/main/scala/org/bitcoins/crypto/SHA256.scala
Normal file
17
crypto/.js/src/main/scala/org/bitcoins/crypto/SHA256.scala
Normal file
|
@ -0,0 +1,17 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.annotation._
|
||||
|
||||
/** Scala wrapper for
|
||||
* https://github.com/bcoin-org/bcrypto/blob/4db0feecde86bce71b0c33d31f7178fb14e7381f/lib/js/sha256.js#L54
|
||||
*/
|
||||
@js.native
|
||||
@JSImport("bcrypto/lib/js/sha256.js", JSImport.Default)
|
||||
class SHA256() extends Hasher {
|
||||
override def init(): Unit = js.native
|
||||
|
||||
override def update(bytes: Buffer): Unit = js.native
|
||||
|
||||
override def `final`(): Buffer = js.native
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
import org.bouncycastle.crypto.digests.SHA256Digest
|
||||
import org.bouncycastle.crypto.params.{
|
||||
ECPrivateKeyParameters,
|
||||
|
@ -11,7 +9,8 @@ import org.bouncycastle.crypto.signers.{ECDSASigner, HMacDSAKCalculator}
|
|||
import org.bouncycastle.math.ec.{ECCurve, ECPoint}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import java.math.BigInteger
|
||||
import scala.util.Try
|
||||
|
||||
object BouncyCastleUtil {
|
||||
|
||||
|
@ -23,14 +22,25 @@ object BouncyCastleUtil {
|
|||
}
|
||||
|
||||
def pubKeyTweakMul(publicKey: ECPublicKey, tweak: ByteVector): ECPublicKey = {
|
||||
val point = publicKey.toPoint.multiply(getBigInteger(tweak))
|
||||
ECPublicKey.fromPoint(point, publicKey.isCompressed)
|
||||
val point = decodePoint(publicKey).multiply(getBigInteger(tweak))
|
||||
decodePubKey(point, publicKey.isCompressed)
|
||||
}
|
||||
|
||||
def decodePoint(bytes: ByteVector): ECPoint = {
|
||||
curve.decodePoint(bytes.toArray)
|
||||
}
|
||||
|
||||
def decodePoint(pubKey: ECPublicKey): ECPoint = {
|
||||
decodePoint(pubKey.bytes)
|
||||
}
|
||||
|
||||
def decodePubKey(
|
||||
point: ECPoint,
|
||||
isCompressed: Boolean = true): ECPublicKey = {
|
||||
val bytes = point.getEncoded(isCompressed)
|
||||
ECPublicKey.fromBytes(ByteVector(bytes))
|
||||
}
|
||||
|
||||
def validatePublicKey(bytes: ByteVector): Boolean = {
|
||||
Try(decodePoint(bytes))
|
||||
.map(_.getCurve == curve)
|
||||
|
@ -38,8 +48,8 @@ object BouncyCastleUtil {
|
|||
}
|
||||
|
||||
def pubKeyTweakMul(pubKey: ECPublicKey, tweak: FieldElement): ECPublicKey = {
|
||||
val tweakedPoint = pubKey.toPoint.multiply(tweak.toBigInteger)
|
||||
ECPublicKey.fromPoint(tweakedPoint, pubKey.isCompressed)
|
||||
val tweakedPoint = decodePoint(pubKey).multiply(tweak.toBigInteger)
|
||||
decodePubKey(tweakedPoint, pubKey.isCompressed)
|
||||
}
|
||||
|
||||
def decompressPublicKey(publicKey: ECPublicKey): ECPublicKey = {
|
||||
|
@ -144,81 +154,6 @@ object BouncyCastleUtil {
|
|||
}
|
||||
resultTry.getOrElse(false)
|
||||
}
|
||||
|
||||
def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val nonceKey =
|
||||
SchnorrNonce.kFromBipSchnorr(privateKey, dataToSign, auxRand)
|
||||
|
||||
schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
|
||||
}
|
||||
|
||||
def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
val rx = nonceKey.schnorrNonce
|
||||
val k = nonceKey.nonceKey.fieldElement
|
||||
val x = privateKey.schnorrKey.fieldElement
|
||||
val e = CryptoUtil
|
||||
.sha256SchnorrChallenge(
|
||||
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign)
|
||||
.bytes
|
||||
|
||||
val challenge = x.multiply(FieldElement(e))
|
||||
val sig = k.add(challenge)
|
||||
|
||||
SchnorrDigitalSignature(rx, sig)
|
||||
}
|
||||
|
||||
def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
val rx = signature.rx
|
||||
val sT = Try(signature.sig.toPrivateKey)
|
||||
|
||||
sT match {
|
||||
case Success(s) =>
|
||||
val eBytes = CryptoUtil
|
||||
.sha256SchnorrChallenge(rx.bytes ++ schnorrPubKey.bytes ++ data)
|
||||
.bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
val negE = e.negate
|
||||
|
||||
val sigPoint = s.publicKey
|
||||
val challengePoint = schnorrPubKey.publicKey.tweakMultiply(negE)
|
||||
val computedR = challengePoint.add(sigPoint)
|
||||
val yCoord = computedR.toPoint.getRawYCoord
|
||||
|
||||
yCoord != null && !yCoord.testBitZero() && computedR.schnorrNonce == rx
|
||||
case Failure(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
val eBytes = CryptoUtil
|
||||
.sha256SchnorrChallenge(nonce.bytes ++ pubKey.bytes ++ data)
|
||||
.bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
|
||||
val compressedSigPoint =
|
||||
nonce.publicKey.add(pubKey.publicKey.tweakMultiply(e))
|
||||
|
||||
if (compressed) {
|
||||
compressedSigPoint
|
||||
} else {
|
||||
compressedSigPoint.decompressed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AdaptorStuff {
|
||||
|
@ -230,7 +165,8 @@ object AdaptorStuff {
|
|||
k: FieldElement,
|
||||
r: ECPublicKey,
|
||||
privateKey: ECPrivateKey): FieldElement = {
|
||||
val rx = FieldElement(r.toPoint.getXCoord.toBigInteger)
|
||||
val rx = FieldElement(
|
||||
BouncyCastleUtil.decodePoint(r).getXCoord.toBigInteger)
|
||||
val x = privateKey.fieldElement
|
||||
val m = FieldElement(dataToSign)
|
||||
val kInv = k.inverse
|
||||
|
@ -243,7 +179,9 @@ object AdaptorStuff {
|
|||
adaptorPoint: ECPublicKey,
|
||||
dataToSign: ByteVector): ECAdaptorSignature = {
|
||||
// Include dataToSign and adaptor in nonce derivation
|
||||
val hash = CryptoUtil.sha256(dataToSign ++ serializePoint(adaptorPoint))
|
||||
val hash =
|
||||
BouncycastleCryptoRuntime.sha256(
|
||||
dataToSign ++ serializePoint(adaptorPoint))
|
||||
val k = DLEQStuff.dleqNonceFunc(hash.bytes,
|
||||
privateKey.fieldElement,
|
||||
"ECDSAAdaptorNon")
|
||||
|
@ -361,7 +299,7 @@ object DLEQStuff {
|
|||
fe: FieldElement,
|
||||
algoName: String): FieldElement = {
|
||||
val kBytes =
|
||||
CryptoUtil.taggedSha256(fe.bytes ++ hash, algoName).bytes
|
||||
BouncycastleCryptoRuntime.taggedSha256(fe.bytes ++ hash, algoName).bytes
|
||||
FieldElement(kBytes)
|
||||
}
|
||||
|
||||
|
@ -372,7 +310,7 @@ object DLEQStuff {
|
|||
r2: ECPublicKey,
|
||||
p1: ECPublicKey,
|
||||
p2: ECPublicKey): ByteVector = {
|
||||
CryptoUtil
|
||||
BouncycastleCryptoRuntime
|
||||
.taggedSha256(
|
||||
serializePoint(adaptorPoint) ++ serializePoint(r1) ++ serializePoint(
|
||||
r2) ++ serializePoint(p1) ++ serializePoint(p2),
|
||||
|
@ -392,7 +330,7 @@ object DLEQStuff {
|
|||
|
||||
// hash(Y || fe*G || fe*Y)
|
||||
val hash =
|
||||
CryptoUtil
|
||||
BouncycastleCryptoRuntime
|
||||
.sha256(
|
||||
serializePoint(adaptorPoint) ++ serializePoint(p1) ++ serializePoint(
|
||||
p2))
|
|
@ -0,0 +1,319 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair
|
||||
import org.bouncycastle.crypto.digests.{RIPEMD160Digest, SHA512Digest}
|
||||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator
|
||||
import org.bouncycastle.crypto.macs.{HMac, SipHash}
|
||||
import org.bouncycastle.crypto.params.{
|
||||
ECKeyGenerationParameters,
|
||||
ECPrivateKeyParameters,
|
||||
KeyParameter
|
||||
}
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.security.{MessageDigest, SecureRandom}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** This is an implementation of [[CryptoRuntime]] that defaults to Bouncy Castle (https://bouncycastle.org/)
|
||||
* and [[java.security]].
|
||||
*/
|
||||
trait BouncycastleCryptoRuntime extends CryptoRuntime {
|
||||
private[this] lazy val secureRandom = new SecureRandom()
|
||||
|
||||
override val cryptoContext: CryptoContext = CryptoContext.BouncyCastle
|
||||
|
||||
override def freshPrivateKey: ECPrivateKey = {
|
||||
val generator: ECKeyPairGenerator = new ECKeyPairGenerator
|
||||
val keyGenParams: ECKeyGenerationParameters =
|
||||
new ECKeyGenerationParameters(CryptoParams.curve, secureRandom)
|
||||
generator.init(keyGenParams)
|
||||
val keypair: AsymmetricCipherKeyPair = generator.generateKeyPair
|
||||
val privParams: ECPrivateKeyParameters =
|
||||
keypair.getPrivate.asInstanceOf[ECPrivateKeyParameters]
|
||||
val priv: BigInteger = privParams.getD
|
||||
val bytes = ByteVector(priv.toByteArray)
|
||||
ECPrivateKey.fromBytes(bytes)
|
||||
}
|
||||
|
||||
/** @param x x coordinate
|
||||
* @return a tuple (p1, p2) where p1 and p2 are points on the curve and p1.x = p2.x = x
|
||||
* p1.y is even, p2.y is odd
|
||||
*/
|
||||
def recoverPoint(x: BigInteger): (ECPoint, ECPoint) = {
|
||||
val bytes = ByteVector(x.toByteArray)
|
||||
|
||||
val bytes32 = if (bytes.length < 32) {
|
||||
bytes.padLeft(32)
|
||||
} else if (bytes.length == 32) {
|
||||
bytes
|
||||
} else if (bytes.length == 33 && bytes.head == 0.toByte) {
|
||||
bytes.tail
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
s"Field element cannot have more than 32 bytes, got $bytes from $x")
|
||||
}
|
||||
|
||||
(BouncyCastleUtil.decodePoint(ECPublicKey(0x02.toByte +: bytes32)),
|
||||
BouncyCastleUtil.decodePoint(ECPublicKey(0x03.toByte +: bytes32)))
|
||||
}
|
||||
|
||||
override def recoverPublicKey(
|
||||
signature: ECDigitalSignature,
|
||||
message: ByteVector): (ECPublicKey, ECPublicKey) = {
|
||||
|
||||
val curve = CryptoParams.curve
|
||||
val (r, s) = (signature.r.bigInteger, signature.s.bigInteger)
|
||||
|
||||
val m = new BigInteger(1, message.toArray)
|
||||
|
||||
val (p1, p2) = recoverPoint(r)
|
||||
|
||||
val Q1 = p1
|
||||
.multiply(s)
|
||||
.subtract(curve.getG.multiply(m))
|
||||
.multiply(r.modInverse(curve.getN))
|
||||
val Q2 = p2
|
||||
.multiply(s)
|
||||
.subtract(curve.getG.multiply(m))
|
||||
.multiply(r.modInverse(curve.getN))
|
||||
|
||||
val pub1 = BouncyCastleUtil.decodePubKey(Q1)
|
||||
val pub2 = BouncyCastleUtil.decodePubKey(Q2)
|
||||
(pub1, pub2)
|
||||
}
|
||||
|
||||
override def hmac512(key: ByteVector, data: ByteVector): ByteVector = {
|
||||
val hmac512 = new HMac(new SHA512Digest())
|
||||
hmac512.init(new KeyParameter(key.toArray))
|
||||
hmac512.update(data.toArray, 0, data.intSize.get)
|
||||
val output = new Array[Byte](64)
|
||||
hmac512.doFinal(output, 0)
|
||||
ByteVector(output)
|
||||
}
|
||||
|
||||
override def ripeMd160(bytes: ByteVector): RipeMd160Digest = {
|
||||
//from this tutorial http://rosettacode.org/wiki/RIPEMD-160#Scala
|
||||
val messageDigest = new RIPEMD160Digest
|
||||
val raw = bytes.toArray
|
||||
messageDigest.update(raw, 0, raw.length)
|
||||
val out = Array.fill[Byte](messageDigest.getDigestSize)(0)
|
||||
messageDigest.doFinal(out, 0)
|
||||
RipeMd160Digest(ByteVector(out))
|
||||
}
|
||||
|
||||
override def sha256(bytes: ByteVector): Sha256Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-256").digest(bytes.toArray)
|
||||
Sha256Digest(ByteVector(hash))
|
||||
}
|
||||
|
||||
override def sha1(bytes: ByteVector): Sha1Digest = {
|
||||
val hash = MessageDigest.getInstance("SHA-1").digest(bytes.toArray).toList
|
||||
Sha1Digest(ByteVector(hash))
|
||||
}
|
||||
|
||||
override def normalize(str: String): String = {
|
||||
java.text.Normalizer.normalize(str, java.text.Normalizer.Form.NFC)
|
||||
}
|
||||
|
||||
override def sha256Hash160(bytes: ByteVector): Sha256Hash160Digest = {
|
||||
val hash = ripeMd160(sha256(bytes).bytes).bytes
|
||||
Sha256Hash160Digest(hash)
|
||||
}
|
||||
|
||||
override def toPublicKey(
|
||||
privateKey: ECPrivateKey,
|
||||
isCompressed: Boolean): ECPublicKey =
|
||||
BouncyCastleUtil.computePublicKey(privateKey)
|
||||
|
||||
override def sign(
|
||||
privateKey: ECPrivateKey,
|
||||
dataToSign: ByteVector): ECDigitalSignature = {
|
||||
BouncyCastleUtil.sign(dataToSign, privateKey)
|
||||
}
|
||||
|
||||
override def signWithEntropy(
|
||||
privateKey: ECPrivateKey,
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature =
|
||||
BouncyCastleUtil.signWithEntropy(bytes, privateKey, entropy)
|
||||
|
||||
override def secKeyVerify(privateKeyBytes: ByteVector): Boolean =
|
||||
CryptoParams.curve.getCurve
|
||||
.isValidFieldElement(new BigInteger(1, privateKeyBytes.toArray))
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
BouncyCastleUtil.verifyDigitalSignature(data, publicKey, signature)
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey =
|
||||
BouncyCastleUtil.decompressPublicKey(publicKey)
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
|
||||
BouncyCastleUtil.computePublicKey(privateKey)
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey =
|
||||
BouncyCastleUtil.pubKeyTweakMul(publicKey, tweak.bytes)
|
||||
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey =
|
||||
pk1.fieldElement.add(pk2.fieldElement).toPrivateKey
|
||||
|
||||
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector = {
|
||||
val sum = pk2.fieldElement.add(FieldElement(pk1))
|
||||
sum.bytes
|
||||
}
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
|
||||
val sumPoint =
|
||||
BouncyCastleUtil.decodePoint(pk1).add(BouncyCastleUtil.decodePoint(pk2))
|
||||
|
||||
BouncyCastleUtil.decodePubKey(sumPoint)
|
||||
}
|
||||
|
||||
def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = {
|
||||
val tweak = privkey.publicKey
|
||||
pubkey.add(tweak)
|
||||
}
|
||||
|
||||
override def isValidPubKey(bytes: ByteVector): Boolean =
|
||||
BouncyCastleUtil.validatePublicKey(bytes)
|
||||
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean =
|
||||
bytes.nonEmpty && isValidPubKey(bytes)
|
||||
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val nonceKey =
|
||||
SchnorrNonce.kFromBipSchnorr(privateKey, dataToSign, auxRand)
|
||||
|
||||
schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
|
||||
}
|
||||
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
val rx = nonceKey.schnorrNonce
|
||||
val k = nonceKey.nonceKey.fieldElement
|
||||
val x = privateKey.schnorrKey.fieldElement
|
||||
val e = sha256SchnorrChallenge(
|
||||
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign).bytes
|
||||
|
||||
val challenge = x.multiply(FieldElement(e))
|
||||
val sig = k.add(challenge)
|
||||
|
||||
SchnorrDigitalSignature(rx, sig)
|
||||
}
|
||||
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
val rx = signature.rx
|
||||
val sT = Try(signature.sig.toPrivateKey)
|
||||
|
||||
sT match {
|
||||
case Success(s) =>
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
rx.bytes ++ schnorrPubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
val negE = e.negate
|
||||
|
||||
val sigPoint = s.publicKey
|
||||
val challengePoint = schnorrPubKey.publicKey.tweakMultiply(negE)
|
||||
val computedR = challengePoint.add(sigPoint)
|
||||
val yCoord = BouncyCastleUtil.decodePoint(computedR).getRawYCoord
|
||||
|
||||
yCoord != null && !yCoord.testBitZero() && computedR.schnorrNonce == rx
|
||||
case Failure(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
val eBytes = sha256SchnorrChallenge(
|
||||
nonce.bytes ++ pubKey.bytes ++ data).bytes
|
||||
|
||||
val e = FieldElement(eBytes)
|
||||
|
||||
val compressedSigPoint =
|
||||
nonce.publicKey.add(pubKey.publicKey.tweakMultiply(e))
|
||||
|
||||
if (compressed) {
|
||||
compressedSigPoint
|
||||
} else {
|
||||
compressedSigPoint.decompressed
|
||||
}
|
||||
}
|
||||
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = {
|
||||
AdaptorStuff.adaptorSign(key, adaptorPoint, msg)
|
||||
}
|
||||
|
||||
override def adaptorComplete(
|
||||
key: ECPrivateKey,
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
|
||||
AdaptorStuff.adaptorComplete(key, adaptorSignature.adaptedSig)
|
||||
}
|
||||
|
||||
override def extractAdaptorSecret(
|
||||
signature: ECDigitalSignature,
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey): ECPrivateKey = {
|
||||
AdaptorStuff.extractAdaptorSecret(signature, adaptorSignature, key)
|
||||
}
|
||||
|
||||
override def adaptorVerify(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean =
|
||||
AdaptorStuff.adaptorVerify(adaptorSignature, key, msg, adaptorPoint)
|
||||
|
||||
override def decodeSignature(
|
||||
signature: ECDigitalSignature): (BigInt, BigInt) =
|
||||
DERSignatureUtil.decodeSignature(signature)
|
||||
|
||||
override def isValidSignatureEncoding(
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
DERSignatureUtil.isValidSignatureEncoding(signature)
|
||||
|
||||
override def isDEREncoded(signature: ECDigitalSignature): Boolean =
|
||||
DERSignatureUtil.isDEREncoded(signature)
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long = {
|
||||
val sipHashCParam = 2
|
||||
val sipHashDParam = 4
|
||||
|
||||
val sh = new SipHash(sipHashCParam, sipHashDParam)
|
||||
|
||||
val keyParam = new KeyParameter(key.bytes.toArray)
|
||||
|
||||
sh.init(keyParam)
|
||||
|
||||
val offset = 0
|
||||
|
||||
sh.update(item.toArray, offset, item.length.toInt)
|
||||
|
||||
sh.doFinal()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BouncycastleCryptoRuntime extends BouncycastleCryptoRuntime
|
|
@ -1,11 +1,11 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
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
|
||||
*/
|
|
@ -0,0 +1,24 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoin.Secp256k1Context
|
||||
|
||||
sealed protected trait CryptoRuntimeFactory {
|
||||
|
||||
def newCryptoRuntime: CryptoRuntime = {
|
||||
val secpDisabled = System.getenv("DISABLE_SECP256K1")
|
||||
if (
|
||||
secpDisabled != null && (secpDisabled.toLowerCase == "true" || secpDisabled == "1")
|
||||
) {
|
||||
BouncycastleCryptoRuntime
|
||||
} else {
|
||||
if (Secp256k1Context.isEnabled) {
|
||||
LibSecp256k1CryptoRuntime
|
||||
} else {
|
||||
BouncycastleCryptoRuntime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected object CryptoRuntimeFactory extends CryptoRuntimeFactory
|
|
@ -1,8 +1,5 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.security.SecureRandom
|
||||
|
||||
import org.bouncycastle.crypto.Digest
|
||||
import org.bouncycastle.crypto.macs.HMac
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
|
@ -10,6 +7,9 @@ import org.bouncycastle.crypto.signers.DSAKCalculator
|
|||
import org.bouncycastle.util.{Arrays, BigIntegers}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import java.security.SecureRandom
|
||||
|
||||
/** Entirely copied from [[org.bouncycastle.crypto.signers.HMacDSAKCalculator HMacDSAKCalculator]]
|
||||
* with an added entropy parameter as well as two lines added adding the entropy to the hash.
|
||||
*
|
|
@ -0,0 +1,283 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** This is an implementation of [[CryptoRuntime]] that defaults to libsecp256k1
|
||||
* (https://github.com/bitcoin-core/secp256k1) when possible. All unsupported functions
|
||||
* are delegated to [[BouncycastleCryptoRuntime]].
|
||||
*/
|
||||
trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
|
||||
|
||||
override val cryptoContext: CryptoContext = CryptoContext.LibSecp256k1
|
||||
|
||||
override def freshPrivateKey: ECPrivateKey =
|
||||
BouncycastleCryptoRuntime.freshPrivateKey
|
||||
|
||||
override def recoverPublicKey(
|
||||
signature: ECDigitalSignature,
|
||||
message: ByteVector): (ECPublicKey, ECPublicKey) =
|
||||
BouncycastleCryptoRuntime.recoverPublicKey(signature, message)
|
||||
|
||||
override def hmac512(key: ByteVector, data: ByteVector): ByteVector =
|
||||
BouncycastleCryptoRuntime.hmac512(key, data)
|
||||
|
||||
override def ripeMd160(bytes: ByteVector): RipeMd160Digest = {
|
||||
BouncycastleCryptoRuntime.ripeMd160(bytes)
|
||||
}
|
||||
|
||||
override def sha256(bytes: ByteVector): Sha256Digest = {
|
||||
BouncycastleCryptoRuntime.sha256(bytes)
|
||||
}
|
||||
|
||||
override def sha1(bytes: ByteVector): Sha1Digest = {
|
||||
BouncycastleCryptoRuntime.sha1(bytes)
|
||||
}
|
||||
|
||||
override def normalize(str: String): String = {
|
||||
BouncycastleCryptoRuntime.normalize(str)
|
||||
}
|
||||
|
||||
override def sha256Hash160(bytes: ByteVector): Sha256Hash160Digest = {
|
||||
BouncycastleCryptoRuntime.sha256Hash160(bytes)
|
||||
}
|
||||
|
||||
override def toPublicKey(
|
||||
privateKey: ECPrivateKey,
|
||||
isCompressed: Boolean): ECPublicKey = {
|
||||
val pubKeyBytes: Array[Byte] =
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray, isCompressed)
|
||||
val pubBytes = ByteVector(pubKeyBytes)
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
s"secp256k1 failed to generate a valid public key, got: ${CryptoBytesUtil
|
||||
.encodeHex(pubBytes)}")
|
||||
ECPublicKey(pubBytes)
|
||||
}
|
||||
|
||||
override def sign(
|
||||
privateKey: ECPrivateKey,
|
||||
dataToSign: ByteVector): ECDigitalSignature = {
|
||||
val signature =
|
||||
NativeSecp256k1.sign(dataToSign.toArray, privateKey.bytes.toArray)
|
||||
ECDigitalSignature(ByteVector(signature))
|
||||
}
|
||||
|
||||
override def signWithEntropy(
|
||||
privateKey: ECPrivateKey,
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
val sigBytes = NativeSecp256k1.signWithEntropy(bytes.toArray,
|
||||
privateKey.bytes.toArray,
|
||||
entropy.toArray)
|
||||
|
||||
ECDigitalSignature(ByteVector(sigBytes))
|
||||
}
|
||||
|
||||
override def secKeyVerify(privateKeyBytes: ByteVector): Boolean =
|
||||
NativeSecp256k1.secKeyVerify(privateKeyBytes.toArray)
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
val result =
|
||||
NativeSecp256k1.verify(data.toArray,
|
||||
signature.bytes.toArray,
|
||||
publicKey.bytes.toArray)
|
||||
|
||||
if (!result) {
|
||||
//if signature verification fails with libsecp256k1 we need to use our old
|
||||
//verification function from spongy castle, this is needed because early blockchain
|
||||
//transactions can have weird non strict der encoded digital signatures
|
||||
//bitcoin core implements this functionality here:
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.cpp#L16-L165
|
||||
|
||||
BouncycastleCryptoRuntime.verify(publicKey, data, signature)
|
||||
} else result
|
||||
}
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey = {
|
||||
if (publicKey.isCompressed) {
|
||||
val decompressed = NativeSecp256k1.decompress(publicKey.bytes.toArray)
|
||||
ECPublicKey.fromBytes(ByteVector(decompressed))
|
||||
} else publicKey
|
||||
}
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
|
||||
val pubKeyBytes: Array[Byte] =
|
||||
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
|
||||
privateKey.isCompressed)
|
||||
val pubBytes = ByteVector(pubKeyBytes)
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
s"secp256k1 failed to generate a valid public key, got: ${CryptoBytesUtil
|
||||
.encodeHex(pubBytes)}")
|
||||
ECPublicKey(pubBytes)
|
||||
}
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey = {
|
||||
val mulBytes = NativeSecp256k1.pubKeyTweakMul(publicKey.bytes.toArray,
|
||||
tweak.bytes.toArray,
|
||||
publicKey.isCompressed)
|
||||
ECPublicKey(ByteVector(mulBytes))
|
||||
}
|
||||
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey = {
|
||||
val sumBytes =
|
||||
NativeSecp256k1.privKeyTweakAdd(pk1.bytes.toArray, pk2.bytes.toArray)
|
||||
ECPrivateKey(ByteVector(sumBytes))
|
||||
}
|
||||
|
||||
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector = {
|
||||
val sum =
|
||||
NativeSecp256k1.privKeyTweakAdd(pk1.toArray, pk2.bytes.toArray)
|
||||
ByteVector(sum)
|
||||
}
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey =
|
||||
BouncycastleCryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey = {
|
||||
val tweaked = NativeSecp256k1.pubKeyTweakAdd(pubkey.bytes.toArray,
|
||||
privkey.bytes.toArray,
|
||||
privkey.isCompressed)
|
||||
ECPublicKey(ByteVector(tweaked))
|
||||
}
|
||||
|
||||
override def isValidPubKey(bytes: ByteVector): Boolean = {
|
||||
try {
|
||||
NativeSecp256k1.isValidPubKey(bytes.toArray)
|
||||
} catch {
|
||||
case scala.util.control.NonFatal(_) =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean =
|
||||
bytes.nonEmpty && BouncycastleCryptoRuntime.isValidPubKey(bytes)
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
// val sigBytes =
|
||||
// NativeSecp256k1.schnorrSign(dataToSign.toArray,
|
||||
// privateKey.bytes.toArray,
|
||||
// auxRand.toArray)
|
||||
// SchnorrDigitalSignature(ByteVector(sigBytes))
|
||||
BouncycastleCryptoRuntime.schnorrSign(dataToSign, privateKey, auxRand)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
// val sigBytes =
|
||||
// NativeSecp256k1.schnorrSignWithNonce(dataToSign.toArray,
|
||||
// privateKey.bytes.toArray,
|
||||
// nonceKey.bytes.toArray)
|
||||
// SchnorrDigitalSignature(ByteVector(sigBytes))
|
||||
BouncycastleCryptoRuntime.schnorrSignWithNonce(dataToSign,
|
||||
privateKey,
|
||||
nonceKey)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
BouncycastleCryptoRuntime.schnorrVerify(data, schnorrPubKey, signature)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
BouncycastleCryptoRuntime.schnorrComputeSigPoint(data,
|
||||
nonce,
|
||||
pubKey,
|
||||
compressed)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = {
|
||||
// val sigWithProof = NativeSecp256k1.adaptorSign(key.bytes.toArray,
|
||||
// adaptorPoint.bytes.toArray,
|
||||
// msg.toArray)
|
||||
// ECAdaptorSignature(ByteVector(sigWithProof))
|
||||
BouncycastleCryptoRuntime.adaptorSign(key, adaptorPoint, msg)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def adaptorComplete(
|
||||
key: ECPrivateKey,
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
|
||||
// val sigBytes =
|
||||
// NativeSecp256k1.adaptorAdapt(key.bytes.toArray,
|
||||
// adaptorSignature.adaptedSig.toArray)
|
||||
// ECDigitalSignature.fromBytes(ByteVector(sigBytes))
|
||||
BouncycastleCryptoRuntime.adaptorComplete(key, adaptorSignature)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def extractAdaptorSecret(
|
||||
signature: ECDigitalSignature,
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey): ECPrivateKey = {
|
||||
// val secretBytes = NativeSecp256k1.adaptorExtractSecret(
|
||||
// signature.bytes.toArray,
|
||||
// adaptorSignature.adaptedSig.toArray,
|
||||
// key.bytes.toArray)
|
||||
//
|
||||
// ECPrivateKey(ByteVector(secretBytes))
|
||||
BouncycastleCryptoRuntime.extractAdaptorSecret(signature,
|
||||
adaptorSignature,
|
||||
key)
|
||||
}
|
||||
|
||||
// TODO: add a native implementation
|
||||
override def adaptorVerify(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean = {
|
||||
// NativeSecp256k1.adaptorVerify(adaptorSignature.adaptedSig.toArray,
|
||||
// key.bytes.toArray,
|
||||
// msg.toArray,
|
||||
// adaptorPoint.bytes.toArray,
|
||||
// adaptorSignature.dleqProof.toArray)
|
||||
BouncycastleCryptoRuntime.adaptorVerify(adaptorSignature,
|
||||
key,
|
||||
msg,
|
||||
adaptorPoint)
|
||||
}
|
||||
|
||||
override def decodeSignature(
|
||||
signature: ECDigitalSignature): (BigInt, BigInt) =
|
||||
BouncycastleCryptoRuntime.decodeSignature(signature)
|
||||
|
||||
override def isValidSignatureEncoding(
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
BouncycastleCryptoRuntime.isValidSignatureEncoding(signature)
|
||||
|
||||
override def isDEREncoded(signature: ECDigitalSignature): Boolean =
|
||||
BouncycastleCryptoRuntime.isDEREncoded(signature)
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
BouncycastleCryptoRuntime.sipHash(item, key)
|
||||
}
|
||||
|
||||
object LibSecp256k1CryptoRuntime extends LibSecp256k1CryptoRuntime
|
|
@ -1,11 +1,10 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
import javax.crypto.spec.{IvParameterSpec, SecretKeySpec}
|
||||
import javax.crypto.{BadPaddingException, Cipher, SecretKey}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.spec.{IvParameterSpec, SecretKeySpec}
|
||||
import javax.crypto.{BadPaddingException, Cipher, SecretKey}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** Represents a encrypted cipher text with it's accompanying
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoin.Secp256k1Context
|
||||
|
||||
sealed trait CryptoContext
|
||||
|
||||
object CryptoContext {
|
||||
|
@ -10,26 +8,10 @@ object CryptoContext {
|
|||
|
||||
case object BouncyCastle extends CryptoContext
|
||||
|
||||
def default: CryptoContext = {
|
||||
val secpDisabled = System.getenv("DISABLE_SECP256K1")
|
||||
if (
|
||||
secpDisabled != null && (secpDisabled.toLowerCase == "true" || secpDisabled == "1")
|
||||
) {
|
||||
BouncyCastle
|
||||
} else {
|
||||
if (Secp256k1Context.isEnabled) {
|
||||
LibSecp256k1
|
||||
} else {
|
||||
BouncyCastle
|
||||
}
|
||||
}
|
||||
}
|
||||
case object BCrypto extends CryptoContext
|
||||
|
||||
lazy val cryptoRuntime: CryptoRuntime = CryptoRuntimeFactory.newCryptoRuntime
|
||||
|
||||
lazy val default: CryptoContext = cryptoRuntime.cryptoContext
|
||||
|
||||
/** The platform specific cryptographic functions required to run bitcoin-s */
|
||||
lazy val cryptoRuntime: CryptoRuntime = {
|
||||
default match {
|
||||
case LibSecp256k1 => JvmCryptoRuntime
|
||||
case BouncyCastle => JvmCryptoRuntime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/** Trait that should be extended by specific runtimes like javascript
|
||||
* or the JVM to support crypto functions needed for bitcoin-s
|
||||
*/
|
||||
trait CryptoRuntime {
|
||||
|
||||
val cryptoContext: CryptoContext
|
||||
|
||||
/** Generates a 32 byte private key */
|
||||
def freshPrivateKey: ECPrivateKey
|
||||
|
||||
|
@ -80,7 +79,7 @@ trait CryptoRuntime {
|
|||
}
|
||||
|
||||
def sha256DLCAttestation(str: String): Sha256Digest = {
|
||||
sha256DLCAttestation(CryptoUtil.serializeForHash(str))
|
||||
sha256DLCAttestation(serializeForHash(str))
|
||||
}
|
||||
|
||||
// The tag "DLC/oracle/announcement/v0"
|
||||
|
@ -95,12 +94,6 @@ trait CryptoRuntime {
|
|||
sha256(dlcAnnouncementTagBytes ++ bytes)
|
||||
}
|
||||
|
||||
/** @param x x coordinate
|
||||
* @return a tuple (p1, p2) where p1 and p2 are points on the curve and p1.x = p2.x = x
|
||||
* p1.y is even, p2.y is odd
|
||||
*/
|
||||
def recoverPoint(x: BigInteger): (ECPoint, ECPoint)
|
||||
|
||||
/** Recover public keys from a signature and the message that was signed. This method will return 2 public keys, and the signature
|
||||
* can be verified with both, but only one of them matches that private key that was used to generate the signature.
|
||||
*
|
||||
|
@ -136,4 +129,86 @@ trait CryptoRuntime {
|
|||
def sha256SchnorrNonce(bytes: ByteVector): Sha256Digest = {
|
||||
sha256(schnorrNonceTagBytes ++ bytes)
|
||||
}
|
||||
|
||||
def publicKey(privateKey: ECPrivateKey): ECPublicKey
|
||||
|
||||
def sign(privateKey: ECPrivateKey, dataToSign: ByteVector): ECDigitalSignature
|
||||
|
||||
def signWithEntropy(
|
||||
privateKey: ECPrivateKey,
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature
|
||||
|
||||
def secKeyVerify(privateKeybytes: ByteVector): Boolean
|
||||
|
||||
def verify(
|
||||
publicKey: ECPublicKey,
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean
|
||||
|
||||
def decompressed(publicKey: ECPublicKey): ECPublicKey
|
||||
|
||||
def tweakMultiply(publicKey: ECPublicKey, tweak: FieldElement): ECPublicKey
|
||||
|
||||
def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey
|
||||
|
||||
def add(bytes: ByteVector, pk2: ECPrivateKey): ByteVector
|
||||
|
||||
def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey
|
||||
|
||||
def pubKeyTweakAdd(pubkey: ECPublicKey, privkey: ECPrivateKey): ECPublicKey
|
||||
|
||||
def isValidPubKey(bytes: ByteVector): Boolean
|
||||
|
||||
def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean
|
||||
|
||||
def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature
|
||||
|
||||
def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature
|
||||
|
||||
def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean
|
||||
|
||||
def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey
|
||||
|
||||
def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature
|
||||
|
||||
def adaptorComplete(
|
||||
key: ECPrivateKey,
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature
|
||||
|
||||
def extractAdaptorSecret(
|
||||
signature: ECDigitalSignature,
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey): ECPrivateKey
|
||||
|
||||
def adaptorVerify(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean
|
||||
|
||||
def decodeSignature(signature: ECDigitalSignature): (BigInt, BigInt)
|
||||
|
||||
def isValidSignatureEncoding(signature: ECDigitalSignature): Boolean
|
||||
|
||||
def isDEREncoded(signature: ECDigitalSignature): Boolean
|
||||
|
||||
/** https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki#hashing-data-objects */
|
||||
def sipHash(item: ByteVector, key: SipHashKey): Long
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/** Utility cryptographic functions
|
||||
* This is a proxy for the underlying implementation of [[CryptoRuntime]]
|
||||
* such as [[JvmCryptoRuntime]].
|
||||
* such as [[LibSecp256k1CryptoRuntime]].
|
||||
*
|
||||
* This is necessary so that the core module doesn't need to be refactored
|
||||
* to add support for multiple platforms, it can keep referencing CryptoUtil
|
||||
|
@ -17,6 +14,8 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
/** The underlying runtime for the specific platform we are running on */
|
||||
private lazy val cryptoRuntime: CryptoRuntime = CryptoContext.cryptoRuntime
|
||||
|
||||
override lazy val cryptoContext: CryptoContext = cryptoRuntime.cryptoContext
|
||||
|
||||
override def freshPrivateKey: ECPrivateKey = {
|
||||
cryptoRuntime.freshPrivateKey
|
||||
}
|
||||
|
@ -41,7 +40,7 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
}
|
||||
|
||||
def doubleSHA256(str: String): DoubleSha256Digest = {
|
||||
doubleSHA256(serializeForHash(str))
|
||||
cryptoRuntime.doubleSHA256(serializeForHash(str))
|
||||
}
|
||||
|
||||
/** Takes sha256(bytes). */
|
||||
|
@ -50,7 +49,7 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
}
|
||||
|
||||
def taggedSha256(str: String, tag: String): Sha256Digest = {
|
||||
taggedSha256(serializeForHash(str), tag)
|
||||
cryptoRuntime.taggedSha256(serializeForHash(str), tag)
|
||||
}
|
||||
|
||||
/** Performs SHA1(bytes). */
|
||||
|
@ -59,7 +58,7 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
}
|
||||
|
||||
def sha1(str: String): Sha1Digest = {
|
||||
sha1(serializeForHash(str))
|
||||
cryptoRuntime.sha1(serializeForHash(str))
|
||||
}
|
||||
|
||||
/** Performs RIPEMD160(bytes). */
|
||||
|
@ -68,7 +67,7 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
}
|
||||
|
||||
def ripeMd160(str: String): RipeMd160Digest = {
|
||||
ripeMd160(serializeForHash(str))
|
||||
cryptoRuntime.ripeMd160(serializeForHash(str))
|
||||
}
|
||||
|
||||
/** Calculates `HMAC-SHA512(key, data)`
|
||||
|
@ -77,19 +76,125 @@ trait CryptoUtil extends CryptoRuntime {
|
|||
cryptoRuntime.hmac512(key, data)
|
||||
}
|
||||
|
||||
/** @param x x coordinate
|
||||
* @return a tuple (p1, p2) where p1 and p2 are points on the curve and p1.x = p2.x = x
|
||||
* p1.y is even, p2.y is odd
|
||||
*/
|
||||
def recoverPoint(x: BigInteger): (ECPoint, ECPoint) = {
|
||||
cryptoRuntime.recoverPoint(x)
|
||||
}
|
||||
|
||||
override def recoverPublicKey(
|
||||
signature: ECDigitalSignature,
|
||||
message: ByteVector): (ECPublicKey, ECPublicKey) = {
|
||||
cryptoRuntime.recoverPublicKey(signature, message)
|
||||
}
|
||||
|
||||
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
|
||||
cryptoRuntime.publicKey(privateKey)
|
||||
|
||||
override def sign(
|
||||
privateKey: ECPrivateKey,
|
||||
dataToSign: ByteVector): ECDigitalSignature =
|
||||
cryptoRuntime.sign(privateKey, dataToSign)
|
||||
|
||||
override def signWithEntropy(
|
||||
privateKey: ECPrivateKey,
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature =
|
||||
cryptoRuntime.signWithEntropy(privateKey, bytes, entropy)
|
||||
|
||||
override def secKeyVerify(privateKeybytes: ByteVector): Boolean =
|
||||
cryptoRuntime.secKeyVerify(privateKeybytes)
|
||||
|
||||
override def verify(
|
||||
publicKey: ECPublicKey,
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
cryptoRuntime.verify(publicKey, data, signature)
|
||||
|
||||
override def decompressed(publicKey: ECPublicKey): ECPublicKey =
|
||||
cryptoRuntime.decompressed(publicKey)
|
||||
|
||||
override def tweakMultiply(
|
||||
publicKey: ECPublicKey,
|
||||
tweak: FieldElement): ECPublicKey =
|
||||
cryptoRuntime.tweakMultiply(publicKey, tweak)
|
||||
|
||||
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey =
|
||||
cryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector =
|
||||
cryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey =
|
||||
cryptoRuntime.add(pk1, pk2)
|
||||
|
||||
override def pubKeyTweakAdd(
|
||||
pubkey: ECPublicKey,
|
||||
privkey: ECPrivateKey): ECPublicKey =
|
||||
cryptoRuntime.pubKeyTweakAdd(pubkey, privkey)
|
||||
|
||||
override def isValidPubKey(bytes: ByteVector): Boolean =
|
||||
cryptoRuntime.isValidPubKey(bytes)
|
||||
|
||||
override def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean =
|
||||
cryptoRuntime.isFullyValidWithBouncyCastle(bytes)
|
||||
|
||||
override def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature =
|
||||
cryptoRuntime.schnorrSign(dataToSign, privateKey, auxRand)
|
||||
|
||||
override def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
privateKey: ECPrivateKey,
|
||||
nonceKey: ECPrivateKey): SchnorrDigitalSignature =
|
||||
cryptoRuntime.schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
|
||||
|
||||
override def schnorrVerify(
|
||||
data: ByteVector,
|
||||
schnorrPubKey: SchnorrPublicKey,
|
||||
signature: SchnorrDigitalSignature): Boolean =
|
||||
cryptoRuntime.schnorrVerify(data, schnorrPubKey, signature)
|
||||
|
||||
override def schnorrComputeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
pubKey: SchnorrPublicKey,
|
||||
compressed: Boolean): ECPublicKey =
|
||||
cryptoRuntime.schnorrComputeSigPoint(data, nonce, pubKey, compressed)
|
||||
|
||||
override def adaptorSign(
|
||||
key: ECPrivateKey,
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature =
|
||||
cryptoRuntime.adaptorSign(key, adaptorPoint, msg)
|
||||
|
||||
override def adaptorComplete(
|
||||
key: ECPrivateKey,
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature =
|
||||
cryptoRuntime.adaptorComplete(key, adaptorSignature)
|
||||
|
||||
override def extractAdaptorSecret(
|
||||
signature: ECDigitalSignature,
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey): ECPrivateKey =
|
||||
cryptoRuntime.extractAdaptorSecret(signature, adaptorSignature, key)
|
||||
|
||||
override def adaptorVerify(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
key: ECPublicKey,
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey): Boolean =
|
||||
cryptoRuntime.adaptorVerify(adaptorSignature, key, msg, adaptorPoint)
|
||||
|
||||
override def decodeSignature(
|
||||
signature: ECDigitalSignature): (BigInt, BigInt) =
|
||||
cryptoRuntime.decodeSignature(signature)
|
||||
|
||||
override def isValidSignatureEncoding(
|
||||
signature: ECDigitalSignature): Boolean =
|
||||
cryptoRuntime.isValidSignatureEncoding(signature)
|
||||
|
||||
override def isDEREncoded(signature: ECDigitalSignature): Boolean =
|
||||
cryptoRuntime.isDEREncoded(signature)
|
||||
|
||||
override def sipHash(item: ByteVector, key: SipHashKey): Long =
|
||||
cryptoRuntime.sipHash(item, key)
|
||||
}
|
||||
|
||||
object CryptoUtil extends CryptoUtil
|
||||
|
|
|
@ -20,9 +20,9 @@ case class ECAdaptorSignature(bytes: ByteVector) extends NetworkElement {
|
|||
val dleqProofS: FieldElement = FieldElement(dleqProof.drop(33).take(32))
|
||||
val dleqProofE: FieldElement = FieldElement(dleqProof.drop(65))
|
||||
|
||||
require(ECPublicKey.isFullyValidWithBouncyCastle(tweakedNonce.bytes),
|
||||
require(CryptoUtil.isFullyValidWithBouncyCastle(tweakedNonce.bytes),
|
||||
s"Tweaked nonce (R) must be a valid public key: $tweakedNonce")
|
||||
require(ECPublicKey.isFullyValidWithBouncyCastle(untweakedNonce.bytes),
|
||||
require(CryptoUtil.isFullyValidWithBouncyCastle(untweakedNonce.bytes),
|
||||
s"Untweaked nonce (R') must be a valid public key: $untweakedNonce")
|
||||
}
|
||||
|
||||
|
|
|
@ -24,19 +24,19 @@ sealed abstract class ECDigitalSignature extends NetworkElement {
|
|||
* https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-asn-1
|
||||
* @return boolean representing if the signature is a valid
|
||||
*/
|
||||
def isDEREncoded: Boolean = DERSignatureUtil.isDEREncoded(this)
|
||||
def isDEREncoded: Boolean = CryptoUtil.isDEREncoded(this)
|
||||
|
||||
/** Checks if the signature is strictly der encoded as per BIP66
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki]]
|
||||
*/
|
||||
def isStrictEncoded: Boolean = DERSignatureUtil.isValidSignatureEncoding(this)
|
||||
def isStrictEncoded: Boolean = CryptoUtil.isValidSignatureEncoding(this)
|
||||
|
||||
/** Decodes the digital signature into it's r and s points
|
||||
* throws an exception if the given sequence of bytes is not a DER encoded signature
|
||||
* @return the (r,s) values for the elliptic curve digital signature
|
||||
*/
|
||||
lazy val decodeSignature: (BigInt, BigInt) =
|
||||
DERSignatureUtil.decodeSignature(this)
|
||||
CryptoUtil.decodeSignature(this)
|
||||
|
||||
/** Represents the r value found in a elliptic curve digital signature */
|
||||
def r: BigInt = {
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext.Implicits
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -32,27 +29,7 @@ sealed abstract class ECPrivateKey
|
|||
* @return the digital signature
|
||||
*/
|
||||
override def sign(dataToSign: ByteVector): ECDigitalSignature = {
|
||||
sign(dataToSign, CryptoContext.default)
|
||||
}
|
||||
|
||||
def sign(
|
||||
dataToSign: ByteVector,
|
||||
context: CryptoContext): ECDigitalSignature = {
|
||||
require(dataToSign.length == 32 && bytes.length <= 32)
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => signWithSecp(dataToSign)
|
||||
case CryptoContext.BouncyCastle => signWithBouncyCastle(dataToSign)
|
||||
}
|
||||
}
|
||||
|
||||
def signWithSecp(dataToSign: ByteVector): ECDigitalSignature = {
|
||||
val signature =
|
||||
NativeSecp256k1.sign(dataToSign.toArray, bytes.toArray)
|
||||
ECDigitalSignature(ByteVector(signature))
|
||||
}
|
||||
|
||||
def signWithBouncyCastle(dataToSign: ByteVector): ECDigitalSignature = {
|
||||
BouncyCastleUtil.sign(dataToSign, this)
|
||||
CryptoUtil.sign(this, dataToSign)
|
||||
}
|
||||
|
||||
def sign(hash: HashDigest): ECDigitalSignature = sign(hash.bytes)
|
||||
|
@ -64,34 +41,7 @@ sealed abstract class ECPrivateKey
|
|||
override def signWithEntropy(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
signWithEntropy(bytes, entropy, CryptoContext.default)
|
||||
}
|
||||
|
||||
def signWithEntropy(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector,
|
||||
context: CryptoContext): ECDigitalSignature = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => signWithEntropyWithSecp(bytes, entropy)
|
||||
case CryptoContext.BouncyCastle =>
|
||||
signWithEntropyWithBouncyCastle(bytes, entropy)
|
||||
}
|
||||
}
|
||||
|
||||
def signWithEntropyWithSecp(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
val sigBytes = NativeSecp256k1.signWithEntropy(bytes.toArray,
|
||||
this.bytes.toArray,
|
||||
entropy.toArray)
|
||||
|
||||
ECDigitalSignature(ByteVector(sigBytes))
|
||||
}
|
||||
|
||||
def signWithEntropyWithBouncyCastle(
|
||||
bytes: ByteVector,
|
||||
entropy: ByteVector): ECDigitalSignature = {
|
||||
BouncyCastleUtil.signWithEntropy(bytes, this, entropy)
|
||||
CryptoUtil.signWithEntropy(this, bytes, entropy)
|
||||
}
|
||||
|
||||
override def signWithEntropyFunction: (
|
||||
|
@ -106,99 +56,27 @@ sealed abstract class ECPrivateKey
|
|||
schnorrSign(dataToSign, auxRand)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def schnorrSign(
|
||||
dataToSign: ByteVector,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
schnorrSignWithBouncyCastle(dataToSign, auxRand)
|
||||
CryptoUtil.schnorrSign(dataToSign, this, auxRand)
|
||||
}
|
||||
|
||||
/*
|
||||
def schnorrSignWithSecp(
|
||||
dataToSign: ByteVector,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
val sigBytes =
|
||||
NativeSecp256k1.schnorrSign(dataToSign.toArray,
|
||||
bytes.toArray,
|
||||
auxRand.toArray)
|
||||
SchnorrDigitalSignature(ByteVector(sigBytes))
|
||||
}
|
||||
*/
|
||||
|
||||
def schnorrSignWithBouncyCastle(
|
||||
dataToSign: ByteVector,
|
||||
auxRand: ByteVector): SchnorrDigitalSignature = {
|
||||
BouncyCastleUtil.schnorrSign(dataToSign, this, auxRand)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def schnorrSignWithNonce(
|
||||
dataToSign: ByteVector,
|
||||
nonce: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
schnorrSignWithNonceWithBouncyCastle(dataToSign, nonce)
|
||||
CryptoUtil.schnorrSignWithNonce(dataToSign, this, nonce)
|
||||
}
|
||||
|
||||
/*
|
||||
def schnorrSignWithNonceWithSecp(
|
||||
dataToSign: ByteVector,
|
||||
nonce: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
val sigBytes =
|
||||
NativeSecp256k1.schnorrSignWithNonce(dataToSign.toArray,
|
||||
bytes.toArray,
|
||||
nonce.bytes.toArray)
|
||||
SchnorrDigitalSignature(ByteVector(sigBytes))
|
||||
}
|
||||
*/
|
||||
|
||||
def schnorrSignWithNonceWithBouncyCastle(
|
||||
dataToSign: ByteVector,
|
||||
nonce: ECPrivateKey): SchnorrDigitalSignature = {
|
||||
BouncyCastleUtil.schnorrSignWithNonce(dataToSign, this, nonce)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def adaptorSign(
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = {
|
||||
adaptorSignWithBouncyCastle(adaptorPoint, msg)
|
||||
CryptoUtil.adaptorSign(this, adaptorPoint, msg)
|
||||
}
|
||||
|
||||
/*
|
||||
def adaptorSignWithSecp(
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = {
|
||||
val sigWithProof = NativeSecp256k1.adaptorSign(bytes.toArray,
|
||||
adaptorPoint.bytes.toArray,
|
||||
msg.toArray)
|
||||
ECAdaptorSignature(ByteVector(sigWithProof))
|
||||
}
|
||||
*/
|
||||
|
||||
def adaptorSignWithBouncyCastle(
|
||||
adaptorPoint: ECPublicKey,
|
||||
msg: ByteVector): ECAdaptorSignature = {
|
||||
AdaptorStuff.adaptorSign(this, adaptorPoint, msg)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def completeAdaptorSignature(
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
|
||||
completeAdaptorSignatureWithBouncyCastle(adaptorSignature)
|
||||
}
|
||||
|
||||
/*
|
||||
def completeAdaptorSignatureWithSecp(
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
|
||||
val sigBytes = NativeSecp256k1.adaptorAdapt(
|
||||
bytes.toArray,
|
||||
adaptorSignature.adaptedSig.toArray)
|
||||
ECDigitalSignature.fromBytes(ByteVector(sigBytes))
|
||||
}
|
||||
*/
|
||||
|
||||
def completeAdaptorSignatureWithBouncyCastle(
|
||||
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
|
||||
AdaptorStuff.adaptorComplete(this, adaptorSignature.adaptedSig)
|
||||
CryptoUtil.adaptorComplete(this, adaptorSignature)
|
||||
}
|
||||
|
||||
def completeAdaptorSignature(
|
||||
|
@ -224,60 +102,25 @@ sealed abstract class ECPrivateKey
|
|||
}
|
||||
}
|
||||
|
||||
// CryptoParams.curve.getN
|
||||
private val N: BigInteger = new BigInteger(
|
||||
"115792089237316195423570985008687907852837564279074904382605163141518161494337")
|
||||
|
||||
def negate: ECPrivateKey = {
|
||||
val negPrivKeyNum = CryptoParams.curve.getN
|
||||
.subtract(new BigInteger(1, bytes.toArray))
|
||||
val negPrivKeyNum = N.subtract(new BigInteger(1, bytes.toArray))
|
||||
ECPrivateKey(ByteVector(negPrivKeyNum.toByteArray))
|
||||
}
|
||||
|
||||
def add(other: ECPrivateKey): ECPrivateKey = {
|
||||
add(other, CryptoContext.default)
|
||||
}
|
||||
|
||||
def add(other: ECPrivateKey, context: CryptoContext): ECPrivateKey = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => addWithSecp(other)
|
||||
case CryptoContext.BouncyCastle => addWithBouncyCastle(other)
|
||||
}
|
||||
}
|
||||
|
||||
def addWithSecp(other: ECPrivateKey): ECPrivateKey = {
|
||||
val sumBytes =
|
||||
NativeSecp256k1.privKeyTweakAdd(bytes.toArray, other.bytes.toArray)
|
||||
ECPrivateKey(ByteVector(sumBytes))
|
||||
}
|
||||
|
||||
def addWithBouncyCastle(other: ECPrivateKey): ECPrivateKey = {
|
||||
fieldElement.add(other.fieldElement).toPrivateKey
|
||||
CryptoUtil.add(this, other)
|
||||
}
|
||||
|
||||
/** Signifies if the this private key corresponds to a compressed public key */
|
||||
def isCompressed: Boolean
|
||||
|
||||
override def publicKey: ECPublicKey = publicKey(CryptoContext.default)
|
||||
|
||||
/** Derives the public for a the private key */
|
||||
def publicKey(context: CryptoContext): ECPublicKey = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => publicKeyWithSecp
|
||||
case CryptoContext.BouncyCastle => publicKeyWithBouncyCastle
|
||||
}
|
||||
}
|
||||
|
||||
def publicKeyWithSecp: ECPublicKey = {
|
||||
val pubKeyBytes: Array[Byte] =
|
||||
NativeSecp256k1.computePubkey(bytes.toArray, isCompressed)
|
||||
val pubBytes = ByteVector(pubKeyBytes)
|
||||
require(
|
||||
ECPublicKey.isFullyValid(pubBytes),
|
||||
s"secp256k1 failed to generate a valid public key, got: ${CryptoBytesUtil
|
||||
.encodeHex(pubBytes)}")
|
||||
ECPublicKey(pubBytes)
|
||||
}
|
||||
|
||||
def publicKeyWithBouncyCastle: ECPublicKey = {
|
||||
BouncyCastleUtil.computePublicKey(this)
|
||||
}
|
||||
override def publicKey: ECPublicKey =
|
||||
CryptoUtil.publicKey(this)
|
||||
|
||||
def schnorrPublicKey: SchnorrPublicKey = {
|
||||
SchnorrPublicKey(publicKey.bytes)
|
||||
|
@ -299,15 +142,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
|
|||
isCompressed: Boolean,
|
||||
ec: ExecutionContext)
|
||||
extends ECPrivateKey {
|
||||
CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 =>
|
||||
require(NativeSecp256k1.secKeyVerify(bytes.toArray),
|
||||
s"Invalid key according to secp256k1, hex: ${bytes.toHex}")
|
||||
case CryptoContext.BouncyCastle =>
|
||||
require(CryptoParams.curve.getCurve
|
||||
.isValidFieldElement(new BigInteger(1, bytes.toArray)),
|
||||
s"Invalid key according to Bouncy Castle, hex: ${bytes.toHex}")
|
||||
}
|
||||
require(CryptoUtil.secKeyVerify(bytes), s"Invalid key, hex: ${bytes.toHex}")
|
||||
}
|
||||
|
||||
def apply(bytes: ByteVector, isCompressed: Boolean)(implicit
|
||||
|
@ -368,41 +203,7 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
* [[org.bitcoins.crypto.ECPublicKey ECPublicKey]].
|
||||
*/
|
||||
def verify(data: ByteVector, signature: ECDigitalSignature): Boolean = {
|
||||
verify(data, signature, CryptoContext.default)
|
||||
}
|
||||
|
||||
def verify(
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature,
|
||||
context: CryptoContext): Boolean = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => verifyWithSecp(data, signature)
|
||||
case CryptoContext.BouncyCastle => verifyWithBouncyCastle(data, signature)
|
||||
}
|
||||
}
|
||||
|
||||
def verifyWithSecp(
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
val result =
|
||||
NativeSecp256k1.verify(data.toArray,
|
||||
signature.bytes.toArray,
|
||||
bytes.toArray)
|
||||
|
||||
if (!result) {
|
||||
//if signature verification fails with libsecp256k1 we need to use our old
|
||||
//verification function from spongy castle, this is needed because early blockchain
|
||||
//transactions can have weird non strict der encoded digital signatures
|
||||
//bitcoin core implements this functionality here:
|
||||
//https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.cpp#L16-L165
|
||||
verifyWithBouncyCastle(data, signature)
|
||||
} else result
|
||||
}
|
||||
|
||||
def verifyWithBouncyCastle(
|
||||
data: ByteVector,
|
||||
signature: ECDigitalSignature): Boolean = {
|
||||
BouncyCastleUtil.verifyDigitalSignature(data, this, signature)
|
||||
CryptoUtil.verify(this, data, signature)
|
||||
}
|
||||
|
||||
def verify(hex: String, signature: ECDigitalSignature): Boolean =
|
||||
|
@ -425,58 +226,17 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
|
||||
def schnorrNonce: SchnorrNonce = SchnorrNonce(bytes)
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def adaptorVerify(
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey,
|
||||
adaptorSignature: ECAdaptorSignature): Boolean = {
|
||||
adaptorVerifyWithBouncyCastle(msg, adaptorPoint, adaptorSignature)
|
||||
CryptoUtil.adaptorVerify(adaptorSignature, this, msg, adaptorPoint)
|
||||
}
|
||||
|
||||
/*
|
||||
def adaptorVerifyWithSecp(
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey,
|
||||
adaptorSignature: ECAdaptorSignature): Boolean = {
|
||||
NativeSecp256k1.adaptorVerify(adaptorSignature.adaptedSig.toArray,
|
||||
bytes.toArray,
|
||||
msg.toArray,
|
||||
adaptorPoint.bytes.toArray,
|
||||
adaptorSignature.dleqProof.toArray)
|
||||
}
|
||||
*/
|
||||
|
||||
def adaptorVerifyWithBouncyCastle(
|
||||
msg: ByteVector,
|
||||
adaptorPoint: ECPublicKey,
|
||||
adaptorSignature: ECAdaptorSignature): Boolean = {
|
||||
AdaptorStuff.adaptorVerify(adaptorSignature, this, msg, adaptorPoint)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def extractAdaptorSecret(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
signature: ECDigitalSignature): ECPrivateKey = {
|
||||
extractAdaptorSecretWithBouncyCastle(adaptorSignature, signature)
|
||||
}
|
||||
|
||||
/*
|
||||
def extractAdaptorSecretWithSecp(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
signature: ECDigitalSignature): ECPrivateKey = {
|
||||
val secretBytes = NativeSecp256k1.adaptorExtractSecret(
|
||||
signature.bytes.toArray,
|
||||
adaptorSignature.adaptedSig.toArray,
|
||||
bytes.toArray)
|
||||
|
||||
ECPrivateKey(ByteVector(secretBytes))
|
||||
}
|
||||
*/
|
||||
|
||||
def extractAdaptorSecretWithBouncyCastle(
|
||||
adaptorSignature: ECAdaptorSignature,
|
||||
signature: ECDigitalSignature): ECPrivateKey = {
|
||||
AdaptorStuff.extractAdaptorSecret(signature, adaptorSignature, this)
|
||||
CryptoUtil.extractAdaptorSecret(signature, adaptorSignature, this)
|
||||
}
|
||||
|
||||
override def toString: String = "ECPublicKey(" + hex + ")"
|
||||
|
@ -488,72 +248,19 @@ sealed abstract class ECPublicKey extends BaseECKey {
|
|||
def isFullyValid: Boolean = ECPublicKey.isFullyValid(bytes)
|
||||
|
||||
/** Returns the decompressed version of this [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] */
|
||||
def decompressed: ECPublicKey = decompressed(CryptoContext.default)
|
||||
|
||||
def decompressed(context: CryptoContext): ECPublicKey = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => decompressedWithSecp
|
||||
case CryptoContext.BouncyCastle => decompressedWithBouncyCastle
|
||||
}
|
||||
}
|
||||
|
||||
def decompressedWithSecp: ECPublicKey = {
|
||||
if (isCompressed) {
|
||||
val decompressed = NativeSecp256k1.decompress(bytes.toArray)
|
||||
ECPublicKey.fromBytes(ByteVector(decompressed))
|
||||
} else this
|
||||
}
|
||||
|
||||
def decompressedWithBouncyCastle: ECPublicKey = {
|
||||
BouncyCastleUtil.decompressPublicKey(this)
|
||||
}
|
||||
|
||||
/** Decodes a [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] in bitcoin-s
|
||||
* to a [[org.bouncycastle.math.ec.ECPoint ECPoint]] data structure that is internal to the
|
||||
* bouncy castle library
|
||||
* @return
|
||||
*/
|
||||
def toPoint: ECPoint = {
|
||||
BouncyCastleUtil.decodePoint(bytes)
|
||||
}
|
||||
def decompressed: ECPublicKey =
|
||||
CryptoUtil.decompressed(this)
|
||||
|
||||
/** Adds this ECPublicKey to another as points and returns the resulting ECPublicKey.
|
||||
*
|
||||
* Note: if this ever becomes a bottleneck, secp256k1_ec_pubkey_combine should
|
||||
* get wrapped in NativeSecp256k1 to speed things up.
|
||||
*/
|
||||
def add(otherKey: ECPublicKey): ECPublicKey = {
|
||||
addWithBouncyCastle(otherKey)
|
||||
}
|
||||
|
||||
def addWithBouncyCastle(otherKey: ECPublicKey): ECPublicKey = {
|
||||
val sumPoint = toPoint.add(otherKey.toPoint)
|
||||
|
||||
ECPublicKey.fromPoint(sumPoint)
|
||||
}
|
||||
def add(otherKey: ECPublicKey): ECPublicKey =
|
||||
CryptoUtil.add(this, otherKey)
|
||||
|
||||
def tweakMultiply(tweak: FieldElement): ECPublicKey = {
|
||||
tweakMultiply(tweak, CryptoContext.default)
|
||||
}
|
||||
|
||||
def tweakMultiply(
|
||||
tweak: FieldElement,
|
||||
context: CryptoContext): ECPublicKey = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => tweakMultiplyWithSecp(tweak)
|
||||
case CryptoContext.BouncyCastle => tweakMultiplyWithBouncyCastle(tweak)
|
||||
}
|
||||
}
|
||||
|
||||
def tweakMultiplyWithSecp(tweak: FieldElement): ECPublicKey = {
|
||||
val mulBytes = NativeSecp256k1.pubKeyTweakMul(bytes.toArray,
|
||||
tweak.bytes.toArray,
|
||||
isCompressed)
|
||||
ECPublicKey(ByteVector(mulBytes))
|
||||
}
|
||||
|
||||
def tweakMultiplyWithBouncyCastle(tweak: FieldElement): ECPublicKey = {
|
||||
BouncyCastleUtil.pubKeyTweakMul(this, tweak.bytes)
|
||||
CryptoUtil.tweakMultiply(this, tweak)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,40 +294,11 @@ object ECPublicKey extends Factory[ECPublicKey] {
|
|||
* Mimics this function in bitcoin core
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.cpp#L207-L212]]
|
||||
*/
|
||||
def isFullyValid(bytes: ByteVector): Boolean = {
|
||||
isFullyValid(bytes, CryptoContext.default)
|
||||
}
|
||||
|
||||
def isFullyValid(bytes: ByteVector, context: CryptoContext): Boolean = {
|
||||
context match {
|
||||
case CryptoContext.LibSecp256k1 => isFullyValidWithSecp(bytes)
|
||||
case CryptoContext.BouncyCastle => isFullyValidWithBouncyCastle(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
def isFullyValidWithSecp(bytes: ByteVector): Boolean = {
|
||||
try {
|
||||
NativeSecp256k1.isValidPubKey(bytes.toArray) && isValid(bytes)
|
||||
} catch {
|
||||
case scala.util.control.NonFatal(_) =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def isFullyValidWithBouncyCastle(bytes: ByteVector): Boolean = {
|
||||
BouncyCastleUtil.validatePublicKey(bytes) && isValid(bytes)
|
||||
}
|
||||
def isFullyValid(bytes: ByteVector): Boolean =
|
||||
isValid(bytes) && CryptoUtil.isValidPubKey(bytes)
|
||||
|
||||
/** Mimics the CPubKey::IsValid function in Bitcoin core, this is a consensus rule
|
||||
* [[https://github.com/bitcoin/bitcoin/blob/27765b6403cece54320374b37afb01a0cfe571c3/src/pubkey.h#L158]]
|
||||
*/
|
||||
def isValid(bytes: ByteVector): Boolean = bytes.nonEmpty
|
||||
|
||||
/** Creates a [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] from the
|
||||
* [[org.bouncycastle.math.ec.ECPoint ECPoint]] data structure used internally inside of Bouncy Castle
|
||||
*/
|
||||
def fromPoint(p: ECPoint, isCompressed: Boolean = true): ECPublicKey = {
|
||||
val bytes = p.getEncoded(isCompressed)
|
||||
ECPublicKey.fromBytes(ByteVector(bytes))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
import org.bouncycastle.math.ec.ECPoint
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.math.BigInteger
|
||||
import scala.util.Try
|
||||
|
||||
/** Represents integers modulo the secp256k1 field size: pow(2,256) - 0x1000003D1.
|
||||
|
@ -52,8 +50,6 @@ case class FieldElement(bytes: ByteVector) extends NetworkElement {
|
|||
FieldElement.negate(this)
|
||||
}
|
||||
|
||||
def getPoint: ECPoint = FieldElement.computePoint(this)
|
||||
|
||||
def getPublicKey: ECPublicKey = toPrivateKey.publicKey
|
||||
|
||||
def inverse: FieldElement = FieldElement.computeInverse(this)
|
||||
|
@ -92,8 +88,13 @@ object FieldElement extends Factory[FieldElement] {
|
|||
val nMinusOne: FieldElement = FieldElement(
|
||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140")
|
||||
|
||||
private val G: ECPoint = CryptoParams.curve.getG
|
||||
private val N: BigInteger = CryptoParams.curve.getN
|
||||
// CryptoParams.curve.getG
|
||||
private val G: ECPublicKey = ECPublicKey(
|
||||
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
|
||||
|
||||
// CryptoParams.curve.getN
|
||||
private val N: BigInteger = new BigInteger(
|
||||
"115792089237316195423570985008687907852837564279074904382605163141518161494337")
|
||||
|
||||
private def getBigInteger(bytes: ByteVector): BigInteger = {
|
||||
new BigInteger(1, bytes.toArray)
|
||||
|
@ -120,7 +121,7 @@ object FieldElement extends Factory[FieldElement] {
|
|||
FieldElement(neg)
|
||||
}
|
||||
|
||||
def computePoint(fe: FieldElement): ECPoint = G.multiply(fe.toBigInteger)
|
||||
def computePoint(fe: FieldElement): ECPublicKey = G.tweakMultiply(fe)
|
||||
|
||||
/** Computes the inverse (mod M) of the input using the Euclidean Algorithm (log time)
|
||||
* Cribbed from [[https://www.geeksforgeeks.org/multiplicative-inverse-under-modulo-m/]]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import javax.crypto.spec.PBEKeySpec
|
||||
import javax.crypto.{SecretKey, SecretKeyFactory}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
/** @define keyStretch Derives the provided value and salt to a secret key
|
||||
* using the PBKDF2 key derivation function
|
||||
|
|
|
@ -26,7 +26,8 @@ object SchnorrNonce extends Factory[SchnorrNonce] {
|
|||
auxRand: ByteVector): ECPrivateKey = {
|
||||
val privKeyForUse = privKey.schnorrKey
|
||||
|
||||
val randHash = CryptoUtil.sha256SchnorrAuxRand(auxRand).bytes
|
||||
val randHash =
|
||||
CryptoUtil.sha256SchnorrAuxRand(auxRand).bytes
|
||||
val maskedKey = randHash.xor(privKeyForUse.bytes)
|
||||
|
||||
val nonceHash = CryptoUtil.sha256SchnorrNonce(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoin.NativeSecp256k1
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
@ -12,25 +11,8 @@ case class SchnorrPublicKey(bytes: ByteVector) extends NetworkElement {
|
|||
require(Try(publicKey).isSuccess,
|
||||
s"Schnorr public key must be a valid x coordinate, got $bytes")
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def verify(data: ByteVector, signature: SchnorrDigitalSignature): Boolean = {
|
||||
verifyWithBouncyCastle(data, signature)
|
||||
}
|
||||
|
||||
/*
|
||||
def verifyWithSecp(
|
||||
data: ByteVector,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
NativeSecp256k1.schnorrVerify(signature.bytes.toArray,
|
||||
data.toArray,
|
||||
bytes.toArray)
|
||||
}
|
||||
*/
|
||||
|
||||
def verifyWithBouncyCastle(
|
||||
data: ByteVector,
|
||||
signature: SchnorrDigitalSignature): Boolean = {
|
||||
BouncyCastleUtil.schnorrVerify(data, this, signature)
|
||||
CryptoUtil.schnorrVerify(data, this, signature)
|
||||
}
|
||||
|
||||
def computeSigPoint(data: ByteVector, nonce: SchnorrNonce): ECPublicKey = {
|
||||
|
@ -67,44 +49,17 @@ case class SchnorrPublicKey(bytes: ByteVector) extends NetworkElement {
|
|||
this.publicKey.tweakMultiply(aggHashes.fieldElement).add(aggNonces)
|
||||
}
|
||||
|
||||
// TODO: match on CryptoContext once secp version is added
|
||||
def computeSigPoint(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
compressed: Boolean): ECPublicKey = {
|
||||
computeSigPointWithBouncyCastle(data, nonce, compressed)
|
||||
}
|
||||
|
||||
/*
|
||||
def computeSigPointWithSecp(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
compressed: Boolean = true): ECPublicKey = {
|
||||
val sigPointBytes = NativeSecp256k1.schnorrComputeSigPoint(
|
||||
data.toArray,
|
||||
nonce.bytes.toArray,
|
||||
bytes.toArray,
|
||||
compressed)
|
||||
ECPublicKey(ByteVector(sigPointBytes))
|
||||
}
|
||||
*/
|
||||
|
||||
def computeSigPointWithBouncyCastle(
|
||||
data: ByteVector,
|
||||
nonce: SchnorrNonce,
|
||||
compressed: Boolean = true): ECPublicKey = {
|
||||
BouncyCastleUtil.schnorrComputeSigPoint(data, nonce, this, compressed)
|
||||
CryptoUtil.schnorrComputeSigPoint(data, nonce, this, compressed)
|
||||
}
|
||||
|
||||
def publicKey: ECPublicKey = {
|
||||
val pubKeyBytes = ByteVector.fromByte(2) ++ bytes
|
||||
|
||||
val validPubKey = CryptoContext.default match {
|
||||
case CryptoContext.LibSecp256k1 =>
|
||||
NativeSecp256k1.isValidPubKey(pubKeyBytes.toArray)
|
||||
case CryptoContext.BouncyCastle =>
|
||||
BouncyCastleUtil.validatePublicKey(pubKeyBytes)
|
||||
}
|
||||
val validPubKey = CryptoUtil.isValidPubKey(pubKeyBytes)
|
||||
|
||||
require(
|
||||
validPubKey,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.bitcoins.core.gcs
|
||||
package org.bitcoins.crypto
|
||||
|
||||
import org.bitcoins.crypto.NetworkElement
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
case class SipHashKey(bytes: ByteVector) extends NetworkElement {
|
|
@ -74,12 +74,15 @@ Here is an example of calling bouncy castle methods in `ECKey`
|
|||
```scala mdoc:to-string
|
||||
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
val publicKey = privKey.publicKeyWithBouncyCastle
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val publicKey = privKey.publicKey
|
||||
val dataToSign = DoubleSha256Digest.empty
|
||||
|
||||
val signature = privKey.signWithBouncyCastle(dataToSign.bytes)
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val signature = privKey.sign(dataToSign.bytes)
|
||||
|
||||
val verified = publicKey.verifyWithBouncyCastle(dataToSign.bytes, signature)
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val verified = publicKey.verify(dataToSign.bytes, signature)
|
||||
|
||||
println(s"Verified with bouncy castle=${verified}")
|
||||
|
||||
|
|
|
@ -36,3 +36,8 @@ addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1")
|
|||
|
||||
//https://github.com/scalameta/sbt-native-image
|
||||
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.0")
|
||||
|
||||
// Scala.js
|
||||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.0")
|
||||
|
||||
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.bitcoins.testkit.core.gen
|
||||
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.gcs.SipHashKey
|
||||
import org.bitcoins.core.number.{UInt64, UInt8}
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.{core, crypto}
|
||||
|
@ -23,7 +22,8 @@ import org.bitcoins.crypto.{
|
|||
SchnorrPublicKey,
|
||||
Sha256Digest,
|
||||
Sha256DigestBE,
|
||||
Sha256Hash160Digest
|
||||
Sha256Hash160Digest,
|
||||
SipHashKey
|
||||
}
|
||||
import org.scalacheck.Gen
|
||||
import scodec.bits.{BitVector, ByteVector}
|
||||
|
|
Loading…
Add table
Reference in a new issue