Pubkey Refactor (#2936)

* Removed ExecutionContext from ECKey

* Refactored ECPublicKey to remove compression state and introduced ECPublicKeyBytes to handle cases where serialization of input is important

* Fixed the rest of bitcoin-s so that it passes all tests

* Made all ECKeys into case classes

* Successfully added isFullyValid invariant to ECPublicKey!

* Fixed docs

* Added scaladocs and fixed a RpcPsbtResult bug

* Reject private keys of length < 32 and fix WIF parsing bug
This commit is contained in:
Nadav Kohen 2021-05-06 13:19:52 -05:00 committed by GitHub
parent b854f7b16a
commit 78f4dfb8c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 811 additions and 493 deletions

View file

@ -12,7 +12,7 @@ import org.bitcoins.core.script.constant.{
import org.bitcoins.crypto.{
DoubleSha256DigestBE,
ECDigitalSignature,
ECPublicKey
ECPublicKeyBytes
}
import play.api.libs.json._
import scodec.bits.ByteVector
@ -43,7 +43,7 @@ case class SerializedTransactionWitness(
hex: String,
scriptType: Option[String],
script: Option[Vector[ScriptToken]],
pubKey: Option[ECPublicKey],
pubKey: Option[ECPublicKeyBytes],
signature: Option[ECDigitalSignature],
stack: Option[Vector[ByteVector]])

View file

@ -9,7 +9,7 @@ import org.bitcoins.core.protocol.transaction.{
TransactionOutPoint
}
import org.bitcoins.commons.serializers.JsonWriters._
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKeyBytes}
import play.api.libs.json.{Json, Writes}
import ujson.{Num, Str, Value}
@ -108,7 +108,7 @@ object RpcOpts {
timestamp: UInt32,
redeemscript: Option[ScriptPubKey] = None,
pubkeys: Option[Vector[ScriptPubKey]] = None,
keys: Option[Vector[ECPrivateKey]] = None,
keys: Option[Vector[ECPrivateKeyBytes]] = None,
internal: Option[Boolean] = None,
watchonly: Option[Boolean] = None,
label: Option[String] = None)

View file

@ -7,7 +7,7 @@ import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.script.ScriptType
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
sealed abstract class RpcPsbtResult
@ -89,7 +89,7 @@ final case class AnalyzePsbtInput(
) extends RpcPsbtResult
final case class PsbtMissingData(
pubkeys: Option[Vector[ECPublicKey]],
pubkeys: Option[Vector[ECPublicKeyBytes]],
signatures: Option[Vector[ECDigitalSignature]],
redeemscript: Option[RpcPsbtScript],
witnessscript: Option[RpcPsbtScript]

View file

@ -302,6 +302,13 @@ object JsonReaders {
SerializerUtil.processJsString[ECPublicKey](ECPublicKey.fromHex)(json)
}
implicit object ECPublicKeyBytesReads extends Reads[ECPublicKeyBytes] {
override def reads(json: JsValue): JsResult[ECPublicKeyBytes] =
SerializerUtil.processJsString[ECPublicKeyBytes](
ECPublicKeyBytes.fromHex)(json)
}
implicit object SchnorrPublicKeyReads extends Reads[SchnorrPublicKey] {
override def reads(json: JsValue): JsResult[SchnorrPublicKey] =

View file

@ -68,6 +68,9 @@ object JsonSerializers {
implicit val sha256Hash160DigestReads: Reads[Sha256Hash160Digest] =
Sha256Hash160DigestReads
implicit val eCPublicKeyReads: Reads[ECPublicKey] = ECPublicKeyReads
implicit val eCPublicKeyBytesReads: Reads[ECPublicKeyBytes] =
ECPublicKeyBytesReads
implicit val p2PKHAddressReads: Reads[P2PKHAddress] = P2PKHAddressReads
implicit val p2SHAddressReads: Reads[P2SHAddress] = P2SHAddressReads
@ -631,6 +634,9 @@ object JsonSerializers {
implicit val ecPublicKeyWrites: Writes[ECPublicKey] =
Writes[ECPublicKey](pubKey => JsString(pubKey.hex))
implicit val ecPublicKeyBytesWrites: Writes[ECPublicKeyBytes] =
Writes[ECPublicKeyBytes](pubKey => JsString(pubKey.hex))
implicit val scriptTokenWrites: Writes[ScriptToken] =
Writes[ScriptToken](token => JsString(tokenToString(token)))

View file

@ -37,7 +37,8 @@ class MessageRpcTest extends BitcoindRpcTest {
for {
client <- clientF
signature <- client.signMessageWithPrivKey(privKey, message)
signature <- client.signMessageWithPrivKey(privKey.toPrivateKeyBytes(),
message)
validity <- client.verifyMessage(address, signature, message)
} yield assert(validity)
}

View file

@ -287,7 +287,7 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
val address = P2PKHAddress(publicKey, networkParam)
for {
_ <- client.importPrivKey(ecPrivateKey,
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
rescan = false,
walletNameOpt = Some(walletName))
key <- client.dumpPrivKey(address, Some(walletName))
@ -297,11 +297,15 @@ class MultiWalletRpcTest extends BitcoindFixturesCachedPairV19 {
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat",
Some(walletName))
} yield {
assert(key == ecPrivateKey)
assert(key.toPrivateKey == ecPrivateKey)
val reader = new Scanner(result.filename)
var found = false
while (reader.hasNext) {
if (reader.next == ECPrivateKeyUtil.toWIF(ecPrivateKey, networkParam)) {
if (
reader.next == ECPrivateKeyUtil.toWIF(
ecPrivateKey.toPrivateKeyBytes(),
networkParam)
) {
found = true
}
}

View file

@ -399,18 +399,23 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
val address = P2PKHAddress(publicKey, networkParam)
for {
_ <- client.importPrivKey(ecPrivateKey, rescan = false)
_ <- client.importPrivKey(ecPrivateKey.toPrivateKeyBytes(),
rescan = false)
key <- client.dumpPrivKey(address)
result <-
client
.dumpWallet(
client.getDaemon.datadir.getAbsolutePath + "/wallet_dump.dat")
} yield {
assert(key == ecPrivateKey)
assert(key.toPrivateKey == ecPrivateKey)
val reader = new Scanner(result.filename)
var found = false
while (reader.hasNext) {
if (reader.next == ECPrivateKeyUtil.toWIF(ecPrivateKey, networkParam)) {
if (
reader.next == ECPrivateKeyUtil.toWIF(
ecPrivateKey.toPrivateKeyBytes(),
networkParam)
) {
found = true
}
}
@ -580,13 +585,15 @@ class WalletRpcTest extends BitcoindFixturesCachedPairV21 {
BitcoinAddress.fromScriptPubKey(output.scriptPubKey, RegTest))
} yield {
val partialSig = BitcoinSigner.signSingle(
ECSignatureParams(
P2WPKHV0InputInfo(outPoint, output.value, privKey.publicKey),
prevTx,
privKey,
HashType.sigHashAll),
ECSignatureParams(P2WPKHV0InputInfo(outPoint,
output.value,
privKey.toPrivateKey.publicKey),
prevTx,
privKey.toPrivateKey,
HashType.sigHashAll),
transaction,
isDummySignature = false)
isDummySignature = false
)
signedTx match {
case btx: NonWitnessTransaction =>

View file

@ -12,7 +12,7 @@ import org.bitcoins.core.protocol.transaction.{
TransactionInput,
TransactionOutPoint
}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKey}
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPrivateKeyBytes}
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.testkit.rpc.{
BitcoindFixturesCachedPairV16,
@ -85,7 +85,7 @@ class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
it should "be able to sign a raw transaction with private keys" in {
nodePair: FixtureParam =>
val client = nodePair.node1
val privkeys: List[ECPrivateKey] =
val privkeys: List[ECPrivateKeyBytes] =
List("cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N",
"cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA")
.map(ECPrivateKeyUtil.fromWIFToPrivateKey)
@ -121,7 +121,10 @@ class BitcoindV16RpcClientTest extends BitcoindFixturesCachedPairV16 {
for {
rawTx <- client.createRawTransaction(inputs, outputs)
signed <- client.signRawTransaction(rawTx, utxoDeps, privkeys.toVector)
signed <- client.signRawTransaction(
rawTx,
utxoDeps,
privkeys.toVector.map(_.toPrivateKey))
} yield assert(signed.complete)
}

View file

@ -109,7 +109,10 @@ class BitcoindV17RpcClientTest extends BitcoindFixturesCachedPairV17 {
for {
rawTx <- client.createRawTransaction(inputs, outputs)
signed <-
client.signRawTransactionWithKey(rawTx, privkeys.toVector, utxoDeps)
client.signRawTransactionWithKey(
rawTx,
privkeys.toVector.map(_.toPrivateKey),
utxoDeps)
} yield assert(signed.complete)
}

View file

@ -14,7 +14,7 @@ import org.bitcoins.commons.serializers.JsonSerializers._
import org.bitcoins.core.config._
import org.bitcoins.core.crypto.ECPrivateKeyUtil
import org.bitcoins.core.util.StartStopAsync
import org.bitcoins.crypto.ECPrivateKey
import org.bitcoins.crypto.{ECPrivateKey, ECPrivateKeyBytes}
import org.bitcoins.rpc.BitcoindException
import org.bitcoins.rpc.config.BitcoindAuthCredentials.{
CookieBased,
@ -78,11 +78,23 @@ trait Client
implicit object ECPrivateKeyWrites extends Writes[ECPrivateKey] {
override def writes(o: ECPrivateKey): JsValue =
JsString(ECPrivateKeyUtil.toWIF(o, network))
JsString(ECPrivateKeyUtil.toWIF(o.toPrivateKeyBytes(), network))
}
implicit val eCPrivateKeyWrites: Writes[ECPrivateKey] = ECPrivateKeyWrites
/** This is here (and not in JsonWrriters)
* so that the implicit network val is accessible
*/
implicit object ECPrivateKeyBytesWrites extends Writes[ECPrivateKeyBytes] {
override def writes(o: ECPrivateKeyBytes): JsValue =
JsString(ECPrivateKeyUtil.toWIF(o, network))
}
implicit val eCPrivateKeyBytesWrites: Writes[ECPrivateKeyBytes] =
ECPrivateKeyBytesWrites
implicit val importMultiAddressWrites: Writes[RpcOpts.ImportMultiAddress] =
Json.writes[RpcOpts.ImportMultiAddress]

View file

@ -2,7 +2,7 @@ package org.bitcoins.rpc.client.common
import org.bitcoins.core.crypto.ECPrivateKeyUtil
import org.bitcoins.core.protocol.P2PKHAddress
import org.bitcoins.crypto.ECPrivateKey
import org.bitcoins.crypto.ECPrivateKeyBytes
import play.api.libs.json.JsString
import scala.concurrent.Future
@ -18,7 +18,7 @@ trait MessageRpc { self: Client =>
}
def signMessageWithPrivKey(
key: ECPrivateKey,
key: ECPrivateKeyBytes,
message: String): Future[String] = {
bitcoindCall[String](
"signmessagewithprivkey",

View file

@ -18,7 +18,7 @@ import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.crypto.{
DoubleSha256Digest,
DoubleSha256DigestBE,
ECPrivateKey,
ECPrivateKeyBytes,
ECPublicKey
}
import org.bitcoins.rpc.client.common.BitcoindVersion._
@ -41,7 +41,7 @@ trait WalletRpc { self: Client =>
def dumpPrivKey(
address: BitcoinAddress,
walletNameOpt: Option[String] = None): Future[ECPrivateKey] = {
walletNameOpt: Option[String] = None): Future[ECPrivateKeyBytes] = {
bitcoindCall[String]("dumpprivkey",
List(JsString(address.value)),
uriExtensionOpt = walletNameOpt.map(walletExtension))
@ -197,7 +197,7 @@ trait WalletRpc { self: Client =>
}
def importPrivKey(
key: ECPrivateKey,
key: ECPrivateKeyBytes,
account: String = "",
rescan: Boolean = true,
walletNameOpt: Option[String] = None): Future[Unit] = {

View file

@ -72,7 +72,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
"5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C")
assert(
privKey.hex == "f49addfd726a59abde172c86452f5f73038a02f4415878dc14934175e8418aff")
val pubKey = privKey.publicKey
val pubKey = privKey.publicKeyBytes
val filter1 = filter.insert(pubKey.bytes)
//hex is from bitcoin core
filter1.hex must be("0302c12b080000000000000001")
@ -133,7 +133,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
val filter6 = BloomFilter(10, 0.000001, UInt32.zero, BloomUpdateAll)
//insert the pubkey of spendingTx in the bloom filter
val pubKey = ECPublicKey(
val pubKey = ECPublicKeyBytes(
"046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339")
val filter7 = filter6.insert(pubKey.bytes)
filter7.isRelevant(creditingTx) must be(true)
@ -204,7 +204,7 @@ class BloomFilterTest extends BitcoinSUnitTest {
val block = Block(
"0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000")
val txs = block.transactions
val pubKey = ECPublicKey(
val pubKey = ECPublicKeyBytes(
"044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")
val filter =
BloomFilter(1, 0.00001, UInt32.zero, BloomUpdateNone).insert(pubKey.bytes)

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.protocol.script.{
}
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script.constant.ScriptToken
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
/** Created by chris on 2/29/16.
@ -27,7 +27,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
val p2pkOutput: TransactionOutput = TransactionOutput(
"00f2052a0100000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac")
val p2pkPubKey: ECPublicKey = ECPublicKey(
val p2pkPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
"0311db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c")
val p2pkSig: ECDigitalSignature = ECDigitalSignature(
@ -55,7 +55,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
val p2pkhOutput: TransactionOutput = TransactionOutput(
"00000000000000001976a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac")
val p2pkhPubKey: ECPublicKey = ECPublicKey(
val p2pkhPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
"02a01aaa27b468ec3fb2ae0c2a9fa1d5dce9b79b35062178f479156d8daa6c0e50")
val p2pkhSig: ECDigitalSignature = ECDigitalSignature(
@ -86,10 +86,10 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
MultiSignatureScriptPubKey(
"47522102895a52495c4c370d50e6bef622ff28d87eec2df00c546b8921b6d07e844bfb9c210283fe2cf10b7dba0d635b3e408532183e27cd43adc11e125027107c095a2bfbc552ae")
val p2shMultiPubKey1: ECPublicKey = ECPublicKey(
val p2shMultiPubKey1: ECPublicKeyBytes = ECPublicKeyBytes(
"02895a52495c4c370d50e6bef622ff28d87eec2df00c546b8921b6d07e844bfb9c")
val p2shMultiPubKey2: ECPublicKey = ECPublicKey(
val p2shMultiPubKey2: ECPublicKeyBytes = ECPublicKeyBytes(
"0283fe2cf10b7dba0d635b3e408532183e27cd43adc11e125027107c095a2bfbc5")
val p2shMultiSig1: ECDigitalSignature = ECDigitalSignature(
@ -176,7 +176,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
val p2shwpkhRedeemScript: ScriptPubKey =
ScriptPubKey("16001422fe81d0b50f7a6407d4280e1603c10f5fc3fac8")
val p2shwpkhPubKey: ECPublicKey = ECPublicKey(
val p2shwpkhPubKey: ECPublicKeyBytes = ECPublicKeyBytes(
"029ca7faef43714b34508589f31394e0b0b6ab24dd4e440eaa03e72841d48e50c5")
val p2shwpkhSig: ECDigitalSignature = ECDigitalSignature(
@ -211,10 +211,10 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
MultiSignatureScriptPubKey(
"6952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae")
val p2wshPubKey1: ECPublicKey = ECPublicKey(
val p2wshPubKey1: ECPublicKeyBytes = ECPublicKeyBytes(
"0375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c")
val p2wshPubKey2: ECPublicKey = ECPublicKey(
val p2wshPubKey2: ECPublicKeyBytes = ECPublicKeyBytes(
"03a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff")
val p2wshSig1: ECDigitalSignature = ECDigitalSignature(
@ -261,7 +261,7 @@ class TransactionSignatureCheckerTest extends BitcoinSUnitTest {
}
// Negative Test Cases
val incorrectPubKey: ECPublicKey = ECPublicKey.freshPublicKey
val incorrectPubKey: ECPublicKeyBytes = ECPublicKeyBytes.freshPublicKey
val incorrectOutput: EmptyTransactionOutput.type = EmptyTransactionOutput
val incorrectTx: Transaction = EmptyTransaction
val incorrectInputIndex: UInt32 = UInt32(100)

View file

@ -53,7 +53,7 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
"cTPg4Zc5Jis2EZXy3NXShgbn487GWBTapbU63BerLDZM3w2hQSjC")
val txSignature =
TransactionSignatureCreator.createSig(txSignatureComponent,
privateKey,
privateKey.toPrivateKey,
HashType.sigHashAll)
txSignature.r must be(expectedSig.r)
txSignature.s must be(expectedSig.s)
@ -81,7 +81,7 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
val txSignature =
TransactionSignatureCreator.createSig(txSignatureComponent,
privateKey,
privateKey.toPrivateKey,
HashType.sigHashAll)
txSignature.r must be(expectedSig.r)
txSignature.s must be(expectedSig.s)
@ -97,8 +97,10 @@ class TransactionSignatureCreatorTest extends BitcoinSJvmTest {
val transaction = Transaction(rawTx)
val prevTransaction = Transaction(
"0100000001e4dbac0d73f4e3a9e99e70596a5f81b35a75f95b0474d051fbfd9dc249a5b67e000000006a4730440220486f112aee12997f6e484754d53d5c2158c18cc6d1d3f13aefcdf0ed19c47b290220136133d934d9e79a57408166c39fbce38e217ea9d417cabc20744134f04f06960121021f8cb5c3d611cf24dd665adff3fd540e4c155a05adaa6b672bfa7897c126d9b6feffffff0293d22a00000000001976a914cd0385f813ec73f8fc340b7069daf566878a0d6b88ac40420f000000000017a91480f7a6c14a8407da3546b4abfc3086876ca9a0668700000000")
val privateKey = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
val privateKey = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"cTTh7jNtZhg3vHTjvYK8zcHkLfsMAS8iqL7pfZ6eVAVHHF8fN1qy")
.toPrivateKey
val inputInfo =
P2PKHInputInfo(TransactionOutPoint(prevTransaction.txId, UInt32.zero),

View file

@ -1,8 +1,12 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.config.{MainNet, RegTest, SigNet, TestNet3}
import org.bitcoins.crypto.ECPrivateKey
import org.bitcoins.testkitcore.gen.{ChainParamsGenerator, CryptoGenerators}
import org.bitcoins.crypto.{ECPrivateKey, ECPrivateKeyBytes}
import org.bitcoins.testkitcore.gen.{
ChainParamsGenerator,
CryptoGenerators,
NumberGenerator
}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class WIFEncodingTest extends BitcoinSUnitTest {
@ -15,13 +19,14 @@ class WIFEncodingTest extends BitcoinSUnitTest {
}
it must "serialize a private key to WIF and then be able to deserialize it" in {
val hex = "2cecbfb72f8d5146d7fe7e5a3f80402c6dd688652c332dff2e44618d2d3372"
val privKey = ECPrivateKey(hex)
val hex = "002cecbfb72f8d5146d7fe7e5a3f80402c6dd688652c332dff2e44618d2d3372"
val privKey = ECPrivateKeyBytes(hex)
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
privKeyFromWIF must be(privKey)
val privKeyDecompressed = ECPrivateKey.fromHex(hex, isCompressed = false)
val privKeyDecompressed =
ECPrivateKey.fromHex(hex).toPrivateKeyBytes(isCompressed = false)
val wifDecompressed = ECPrivateKeyUtil.toWIF(privKeyDecompressed, TestNet3)
val privKeyDecompressedFromWIF =
ECPrivateKeyUtil.fromWIFToPrivateKey(wifDecompressed)
@ -31,7 +36,7 @@ class WIFEncodingTest extends BitcoinSUnitTest {
it must "serialize a private key to WIF when the private key is prefixed with 0 bytes" in {
val hex =
"00fc391adf4d6063a16a2e38b14d2be10133c4dacd4348b49d23ee0ce5ff4f1701"
val privKey = ECPrivateKey(hex)
val privKey = ECPrivateKeyBytes(hex)
val wif = ECPrivateKeyUtil.toWIF(privKey, TestNet3)
val privKeyFromWIF = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
privKeyFromWIF must be(privKey)
@ -48,24 +53,42 @@ class WIFEncodingTest extends BitcoinSUnitTest {
it must "decode a WIF private key corresponding to uncompressed public key" in {
val wif = "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"
val privKey = ECPrivateKeyUtil.fromWIFToPrivateKey(wif)
privKey.publicKey.hex must be(
privKey.publicKeyBytes.hex must be(
"045b81f0017e2091e2edcd5eecf10d5bdd120a5514cb3ee65b8447ec18bfc4575c6d5bf415e54e03b1067934a0f0ba76b01c6b9ab227142ee1d543764b69d901e0")
}
it must "have serialization symmetry for WIF format" in {
forAll(CryptoGenerators.privateKey, ChainParamsGenerator.networkParams) {
(privKey, network) =>
val wif = ECPrivateKeyUtil.toWIF(privKey, network)
network match {
case MainNet =>
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == network)
case TestNet3 | RegTest | SigNet =>
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
}
assert(ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey)
forAll(CryptoGenerators.privateKey,
ChainParamsGenerator.networkParams,
NumberGenerator.bool) { (privKey, network, compressed) =>
val wif =
ECPrivateKeyUtil.toWIF(privKey.toPrivateKeyBytes(compressed), network)
assert(ECPrivateKeyUtil.isCompressed(wif) == compressed)
network match {
case MainNet =>
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == MainNet)
case TestNet3 | RegTest | SigNet =>
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
}
assert(
ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey.toPrivateKeyBytes(
compressed))
}
}
it must "have serialization symmetry for WIF format when private key ends in 0x01" in {
val privKey = ECPrivateKey(
"710ed6c96012015f02e352cddd6f5a5b32499f2926ac7752b57d93b38be8c701")
val wif =
ECPrivateKeyUtil.toWIF(privKey.toPrivateKeyBytes(isCompressed = false),
TestNet3)
assert(!ECPrivateKeyUtil.isCompressed(wif))
assert(ECPrivateKeyUtil.parseNetworkFromWIF(wif).get == TestNet3)
assert(
ECPrivateKeyUtil.fromWIFToPrivateKey(wif) == privKey
.toPrivateKeyBytes(isCompressed = false))
}
it must "fail to parse unknown WIF networks" in {
// Litecoin privkey
val wif = "6uSDaezGtedUbYk4F9CNVXbDWw9DuEuw7czU596t1CzmeAJ77P8"

View file

@ -387,8 +387,10 @@ class HDPathTest extends BitcoinSUnitTest {
val spk = P2WPKHWitnessSPKV0(derivedPub)
val address = Bech32Address(spk, MainNet)
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
"KyZpNDKnfs94vbrwhJneDi77V6jF64PWPF8x5cdJb8ifgg2DUc9d")
val expectedPriv = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"KyZpNDKnfs94vbrwhJneDi77V6jF64PWPF8x5cdJb8ifgg2DUc9d")
.toPrivateKey
val expectedPub = ECPublicKey(
hex"0330d54fd0dd420a6e5f8d3624f5f3482cae350f79d5f0753bf5beef9c2d91af3c")
val expectedAddress =
@ -407,8 +409,10 @@ class HDPathTest extends BitcoinSUnitTest {
val spk = P2WPKHWitnessSPKV0(derivedPub)
val address = Bech32Address(spk, MainNet)
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
"Kxpf5b8p3qX56DKEe5NqWbNUP9MnqoRFzZwHRtsFqhzuvUJsYZCy")
val expectedPriv = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"Kxpf5b8p3qX56DKEe5NqWbNUP9MnqoRFzZwHRtsFqhzuvUJsYZCy")
.toPrivateKey
val expectedPub = ECPublicKey(
hex"03e775fd51f0dfb8cd865d9ff1cca2a158cf651fe997fdc9fee9c1d3b5e995ea77")
val expectedAddress =
@ -427,8 +431,10 @@ class HDPathTest extends BitcoinSUnitTest {
val spk = P2WPKHWitnessSPKV0(derivedPub)
val address = Bech32Address(spk, MainNet)
val expectedPriv = ECPrivateKeyUtil.fromWIFToPrivateKey(
"KxuoxufJL5csa1Wieb2kp29VNdn92Us8CoaUG3aGtPtcF3AzeXvF")
val expectedPriv = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"KxuoxufJL5csa1Wieb2kp29VNdn92Us8CoaUG3aGtPtcF3AzeXvF")
.toPrivateKey
val expectedPub = ECPublicKey(
hex"03025324888e429ab8e3dbaf1f7802648b9cd01e9b418485c5fa4c1b9b5700e1a6")
val expectedAddress =

View file

@ -4,7 +4,7 @@ import org.bitcoins.core.bloom._
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
import org.bitcoins.core.util.BytesUtil
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKey}
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKeyBytes}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
/** Created by chris on 8/9/16.
@ -136,7 +136,7 @@ class MerkleBlockTests extends BitcoinSUnitTest {
// Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
// This should not match the third transaction though it spends the output matched
// It will match the fourth transaction, which has another pay-to-pubkey output to the same address
val pubKey = ECPublicKey(
val pubKey = ECPublicKeyBytes(
"044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af")
val filterWithPubKey = filter.insert(pubKey.bytes)

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.testkitcore.util.TestUtil
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.bitcoins.crypto.ECPublicKeyBytes
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
/** Created by chris on 3/8/16.
*/
@ -36,11 +35,11 @@ class MultiSignatureScriptPubKeyTest extends BitcoinSUnitTest {
multiSigScriptPubKey.publicKeys must be(
Seq(
ECPublicKey(
ECPublicKeyBytes(
"025878e270211662a27181cf4d6ad4d2cf0e69a98a3815c086f587c7e9388d8718"),
ECPublicKey(
ECPublicKeyBytes(
"03fc85980e3fac1f3d8a5c3223c3ef5bffc1bd42d2cc42add8c3899cc66e7f1906"),
ECPublicKey(
ECPublicKeyBytes(
"0215b5bd050869166a70a7341b4f216e268b7c6c7504576dcea2cce7d11cc9a35f")
))

View file

@ -1,8 +1,7 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.testkitcore.util.TestUtil
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.bitcoins.crypto.ECPublicKeyBytes
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
/** Created by chris on 4/1/16.
*/
@ -15,7 +14,7 @@ class P2PKScriptPubKeyTest extends BitcoinSUnitTest {
throw new RuntimeException("should have been p2pk script pub key")
}
p2pkScriptPubKey.publicKey must be(ECPublicKey(
p2pkScriptPubKey.publicKey must be(ECPublicKeyBytes(
"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"))
}
}

View file

@ -5,7 +5,7 @@ import org.bitcoins.core.script.constant.{
OP_0,
ScriptConstant
}
import org.bitcoins.crypto.ECPublicKey
import org.bitcoins.crypto.ECPublicKeyBytes
import org.bitcoins.testkitcore.util.{BitcoinSJvmTest, TestUtil}
/** Created by chris on 3/8/16.
@ -21,9 +21,9 @@ class P2SHScriptSignatureTest extends BitcoinSJvmTest {
}
p2shScriptSig.publicKeys must be(
Seq(
ECPublicKey(
ECPublicKeyBytes(
"0369d26ebd086523384a0f89f293d4c327a65fa73332d8efd1097cb35231295b83"),
ECPublicKey(
ECPublicKeyBytes(
"02480863e5c4a4e9763f5380c44fcfe6a3b7787397076cf9ea1049303a9d34f721")
))

View file

@ -1,12 +1,12 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.crypto.ECPrivateKey
import org.bitcoins.crypto.ECPrivateKeyBytes
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class P2WPKHWitnessSPKV0Test extends BitcoinSUnitTest {
"P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in {
val uncompressed = ECPrivateKey(false).publicKey
val uncompressed = ECPrivateKeyBytes.freshPrivateKey(false).publicKeyBytes
intercept[IllegalArgumentException] {
P2WPKHWitnessSPKV0(uncompressed)
}

View file

@ -1,13 +1,23 @@
package org.bitcoins.core.protocol.script
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.crypto.ECPrivateKey
import org.bitcoins.core.script.constant.{OP_1, ScriptConstant, ScriptNumber}
import org.bitcoins.core.script.crypto.{OP_CHECKMULTISIG, OP_CHECKSIG}
import org.bitcoins.core.util.BitcoinScriptUtil
import org.bitcoins.crypto.ECPrivateKeyBytes
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class P2WSHWitnessSPKV0Test extends BitcoinSUnitTest {
val uncompressed = ECPrivateKey(false).publicKey
val p2pk = P2PKScriptPubKey(uncompressed)
val multisig = MultiSignatureScriptPubKey(1, Vector(uncompressed))
val uncompressed = ECPrivateKeyBytes.freshPrivateKey(false).publicKeyBytes
val pushOps = BitcoinScriptUtil.calculatePushOp(uncompressed.bytes)
val asmP2PK = pushOps ++ Seq(ScriptConstant(uncompressed.bytes), OP_CHECKSIG)
val p2pk = P2PKScriptPubKey(asmP2PK)
val asmMultisig =
Seq(OP_1) ++ pushOps ++ Seq(ScriptConstant(uncompressed.bytes),
OP_1,
OP_CHECKMULTISIG)
val multisig = MultiSignatureScriptPubKey.fromAsm(asmMultisig)
"P2WPKHWitnessSPKV0" must "fail to be created with an uncompressed public key" in {
intercept[IllegalArgumentException] {
P2WSHWitnessSPKV0(p2pk)

View file

@ -339,10 +339,14 @@ class PSBTUnitTest extends BitcoinSUnitTest {
assert(unsignedPsbt.nextRole == PSBTRole.SignerPSBTRole)
val privKey0 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
val privKey1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
val privKey0 = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
.toPrivateKey
val privKey1 = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
.toPrivateKey
// BCrypto does not use low r signing
val (expectedPsbt0, expectedPsbt1) = CryptoUtil.cryptoContext match {
@ -361,11 +365,15 @@ class PSBTUnitTest extends BitcoinSUnitTest {
(psbt0, psbt1)
}
val privKey2 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au")
val privKey2 = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au")
.toPrivateKey
val privKey3 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE")
val privKey3 = ECPrivateKeyUtil
.fromWIFToPrivateKey(
"cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE")
.toPrivateKey
val expectedPubKeyHashes =
Vector(privKey0, privKey1, privKey2, privKey3).map { key =>

View file

@ -8,10 +8,9 @@ import org.bitcoins.core.script.flag.ScriptVerifyWitnessPubKeyType
import org.bitcoins.core.script.locktime.OP_CHECKLOCKTIMEVERIFY
import org.bitcoins.core.script.reserved.{OP_NOP, OP_RESERVED}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.crypto.{ECPrivateKey, ECPublicKey}
import org.bitcoins.crypto.{ECPrivateKeyBytes, ECPublicKeyBytes}
import org.bitcoins.testkitcore.gen.ScriptGenerators
import org.bitcoins.testkitcore.util.TestUtil
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.bitcoins.testkitcore.util.{BitcoinSUnitTest, TestUtil}
import scodec.bits.ByteVector
/** Created by chris on 3/2/16.
@ -36,7 +35,7 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
// https://en.bitcoin.it/wiki/Genesis_block
it must "filter out non-data from the genesis coinbase transaction" in {
val genesisPK = ECPublicKey.fromHex(
val genesisPK = ECPublicKeyBytes.fromHex(
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")
val output = TransactionOutput.fromHex(
"00f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac")
@ -272,7 +271,7 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
it must "check a public key's encoding" in {
//pubkeys must be compressed or uncompressed or else that are not validly encoded
val key = ECPublicKey("00")
val key = ECPublicKeyBytes("00")
val program = TestUtil.testProgramExecutionInProgress
BitcoinScriptUtil.checkPubKeyEncoding(key, program) must be(false)
}
@ -300,16 +299,16 @@ class BitcoinScriptUtilTest extends BitcoinSUnitTest {
}
it must "determine if a segwit pubkey is compressed" in {
val key = ECPrivateKey(false)
val pubKey = key.publicKey
val key = ECPrivateKeyBytes.freshPrivateKey(false)
val pubKey = key.publicKeyBytes
val flags = Seq(ScriptVerifyWitnessPubKeyType)
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey,
SigVersionWitnessV0,
flags) must be(
Some(ScriptErrorWitnessPubKeyType))
val key2 = ECPrivateKey(false)
val pubKey2 = key2.publicKey
val key2 = ECPrivateKeyBytes.freshPrivateKey(false)
val pubKey2 = key2.publicKeyBytes
BitcoinScriptUtil.isValidPubKeyEncoding(pubKey2,
SigVersionBase,
flags) must be(None)

View file

@ -2,17 +2,17 @@ package org.bitcoins.core.crypto
import org.bitcoins.core.config.{NetworkParameters, Networks}
import org.bitcoins.core.util.{Base58, BytesUtil}
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKey}
import org.bitcoins.crypto.{CryptoUtil, ECPrivateKeyBytes}
import scodec.bits.ByteVector
import scala.util.{Failure, Success, Try}
object ECPrivateKeyUtil {
/** Converts a [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] to
/** Converts a [[org.bitcoins.crypto.ECPrivateKeyBytes ECPrivateKey]] to
* [[https://en.bitcoin.it/wiki/Wallet_import_format WIF]]
*/
def toWIF(privKey: ECPrivateKey, network: NetworkParameters): String = {
def toWIF(privKey: ECPrivateKeyBytes, network: NetworkParameters): String = {
val networkByte = network.privateKey
//append 1 byte to the end of the priv key byte representation if we need a compressed pub key
val fullBytes =
@ -31,10 +31,10 @@ object ECPrivateKeyUtil {
* @param WIF Wallet Import Format. Encoded in Base58
* @return
*/
def fromWIFToPrivateKey(WIF: String): ECPrivateKey = {
def fromWIFToPrivateKey(WIF: String): ECPrivateKeyBytes = {
val isCompressed = ECPrivateKeyUtil.isCompressed(WIF)
val privateKeyBytes = trimFunction(WIF)
ECPrivateKey.fromBytes(privateKeyBytes, isCompressed)
ECPrivateKeyBytes(privateKeyBytes, isCompressed)
}
/** Takes in WIF private key as a sequence of bytes and determines if it corresponds to a compressed public key.
@ -49,7 +49,7 @@ object ECPrivateKeyUtil {
val validCompressedBytesInHex: Seq[String] =
validCompressedBytes.map(b => BytesUtil.encodeHex(b))
val firstByteHex = BytesUtil.encodeHex(bytes.head)
if (validCompressedBytesInHex.contains(firstByteHex))
if (validCompressedBytesInHex.contains(firstByteHex) && bytes.length == 38)
bytes(bytes.length - 5) == 0x01.toByte
else false
}

View file

@ -9,7 +9,12 @@ import org.bitcoins.core.script.crypto._
import org.bitcoins.core.script.flag.{ScriptFlag, ScriptFlagUtil}
import org.bitcoins.core.script.result.ScriptErrorWitnessPubKeyType
import org.bitcoins.core.util.BitcoinScriptUtil
import org.bitcoins.crypto.{DERSignatureUtil, ECDigitalSignature, ECPublicKey}
import org.bitcoins.crypto.{
DERSignatureUtil,
ECDigitalSignature,
ECPublicKey,
ECPublicKeyBytes
}
import scodec.bits.ByteVector
import scala.annotation.tailrec
@ -20,6 +25,13 @@ import scala.annotation.tailrec
*/
trait TransactionSignatureChecker {
def checkSignature(
txSignatureComponent: TxSigComponent,
pubKeyBytes: ECPublicKeyBytes,
signature: ECDigitalSignature): TransactionSignatureCheckerResult =
checkSignature(txSignatureComponent,
PartialSignature(pubKeyBytes, signature))
def checkSignature(
txSignatureComponent: TxSigComponent,
pubKey: ECPublicKey,
@ -58,7 +70,7 @@ trait TransactionSignatureChecker {
def checkSignature(
txSignatureComponent: TxSigComponent,
script: Seq[ScriptToken],
pubKey: ECPublicKey,
pubKey: ECPublicKeyBytes,
signature: ECDigitalSignature,
flags: Seq[ScriptFlag] =
Policy.standardFlags): TransactionSignatureCheckerResult = {
@ -147,7 +159,7 @@ trait TransactionSignatureChecker {
txSignatureComponent: TxSigComponent,
script: Seq[ScriptToken],
sigs: List[ECDigitalSignature],
pubKeys: List[ECPublicKey],
pubKeys: List[ECPublicKeyBytes],
flags: Seq[ScriptFlag],
requiredSigs: Long): TransactionSignatureCheckerResult = {
require(requiredSigs >= 0,

View file

@ -9,9 +9,6 @@ import scodec.bits.ByteVector
* 33 byte compressed secp256k1 public key.
*/
case class NodeId(pubKey: ECPublicKey) extends NetworkElement {
require(
pubKey.isCompressed,
s"Cannot create a nodeId from a public key that was not compressed ${pubKey.hex}")
override def toString: String = pubKey.hex

View file

@ -25,9 +25,6 @@ case class LnRoute(
cltvExpiryDelta: Short)
extends NetworkElement {
require(pubkey.isCompressed,
s"Can only use a compressed public key in routing")
override def bytes: ByteVector = {
val cltvExpiryDeltaHex = BytesUtil.encodeHex(cltvExpiryDelta)

View file

@ -154,11 +154,11 @@ sealed trait MultiSignatureScriptPubKey extends RawScriptPubKey {
}
/** Returns the public keys encoded into the `scriptPubKey` */
def publicKeys: Seq[ECPublicKey] = {
def publicKeys: Seq[ECPublicKeyBytes] = {
asm
.filter(_.isInstanceOf[ScriptConstant])
.slice(1, maxSigs + 1)
.map(key => ECPublicKey(key.hex))
.map(key => ECPublicKeyBytes(key.bytes))
}
override def toString = s"multi($requiredSigs,${publicKeys.mkString(",")})"
@ -343,8 +343,8 @@ object P2SHScriptPubKey extends ScriptFactory[P2SHScriptPubKey] {
*/
sealed trait P2PKScriptPubKey extends RawScriptPubKey {
def publicKey: ECPublicKey =
ECPublicKey(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
def publicKey: ECPublicKeyBytes =
ECPublicKeyBytes(BitcoinScriptUtil.filterPushOps(asm).head.bytes)
override def toString = s"pk(${publicKey.hex})"
@ -382,6 +382,21 @@ object P2PKScriptPubKey extends ScriptFactory[P2PKScriptPubKey] {
case _ => false
}
/** Builds a P2PKScriptPubKey from a specific branch of a P2PKWithTimeout.
* Useful for when decomposing a script into spending branches.
*/
private[core] def fromP2PKWithTimeout(
p2pkWithTimeout: P2PKWithTimeoutScriptPubKey,
timeoutBranch: Boolean): P2PKScriptPubKey = {
val pubKeyBytes =
if (timeoutBranch) p2pkWithTimeout.timeoutPubKey
else p2pkWithTimeout.pubKey
val pushOps = BitcoinScriptUtil.calculatePushOp(pubKeyBytes.bytes)
val asm = pushOps ++ Seq(ScriptConstant(pubKeyBytes.bytes), OP_CHECKSIG)
P2PKScriptPubKey(asm)
}
}
sealed trait LockTimeScriptPubKey extends RawScriptPubKey {
@ -896,8 +911,8 @@ object NonStandardNotIfConditionalScriptPubKey
*/
sealed trait P2PKWithTimeoutScriptPubKey extends RawScriptPubKey {
lazy val pubKey: ECPublicKey =
ECPublicKey.fromBytes(asm(2).bytes)
lazy val pubKey: ECPublicKeyBytes =
ECPublicKeyBytes(asm(2).bytes)
private lazy val smallCSVOpt: Option[Long] = {
asm(4) match {
@ -912,10 +927,10 @@ sealed trait P2PKWithTimeoutScriptPubKey extends RawScriptPubKey {
.getOrElse(ScriptNumber(asm(5).bytes))
}
lazy val timeoutPubKey: ECPublicKey = {
lazy val timeoutPubKey: ECPublicKeyBytes = {
smallCSVOpt match {
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
case None => ECPublicKey.fromBytes(asm(9).bytes)
case Some(_) => ECPublicKeyBytes(asm(8).bytes)
case None => ECPublicKeyBytes(asm(9).bytes)
}
}
}
@ -968,24 +983,28 @@ object P2PKWithTimeoutScriptPubKey
}
if (asm.length == requiredSize) {
val pubKey = ECPublicKey.fromBytes(asm(2).bytes)
val pubKeyTry = Try(ECPublicKey.fromBytes(asm(2).bytes))
val lockTimeTry = smallCSVOpt match {
case Some(num) => Success(ScriptNumber(num))
case None => Try(ScriptNumber.fromBytes(asm(5).bytes))
}
val timeoutPubKey = smallCSVOpt match {
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
case None => ECPublicKey.fromBytes(asm(9).bytes)
val timeoutPubKeyTry = Try {
smallCSVOpt match {
case Some(_) => ECPublicKey.fromBytes(asm(8).bytes)
case None => ECPublicKey.fromBytes(asm(9).bytes)
}
}
lockTimeTry match {
case Success(lockTime) =>
(pubKeyTry, lockTimeTry, timeoutPubKeyTry) match {
case (Failure(_), _, _) => false
case (_, Failure(_), _) => false
case (_, _, Failure(_)) => false
case (Success(pubKey), Success(lockTime), Success(timeoutPubKey)) =>
asm == P2PKWithTimeoutScriptPubKey(pubKey,
lockTime,
timeoutPubKey).asm
case Failure(_) => false
}
} else {
false
@ -1258,12 +1277,17 @@ object P2WPKHWitnessSPKV0 extends ScriptFactory[P2WPKHWitnessSPKV0] {
fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes)))
}
/** Creates a P2WPKH witness script pubkey */
def apply(pubKey: ECPublicKey): P2WPKHWitnessSPKV0 = {
/** Builds a P2WPKH SPK from raw ECPublicKeyBytes (unsafe). */
private[core] def apply(pubKey: ECPublicKeyBytes): P2WPKHWitnessSPKV0 = {
//https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type
require(
pubKey.isCompressed,
s"Public key must be compressed to be used in a segwit script, see BIP143")
P2WPKHWitnessSPKV0(pubKey.toPublicKey)
}
/** Creates a P2WPKH witness script pubkey */
def apply(pubKey: ECPublicKey): P2WPKHWitnessSPKV0 = {
val hash = CryptoUtil.sha256Hash160(pubKey.bytes)
val pushop = BitcoinScriptUtil.calculatePushOp(hash.bytes)
fromAsm(Seq(OP_0) ++ pushop ++ Seq(ScriptConstant(hash.bytes)))

View file

@ -4,7 +4,7 @@ import org.bitcoins.core.script.constant._
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.util._
import org.bitcoins.core.wallet.utxo.ConditionalPath
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey, ECPublicKeyBytes}
import scala.annotation.tailrec
import scala.util.Try
@ -79,7 +79,7 @@ sealed trait P2PKHScriptSignature extends ScriptSignature {
def signature: ECDigitalSignature = signatures.head
/** Gives us the public key inside of a p2pkh script signature */
def publicKey: ECPublicKey = ECPublicKey(asm.last.bytes)
def publicKey: ECPublicKeyBytes = ECPublicKeyBytes(asm.last.bytes)
override def signatures: Seq[ECDigitalSignature] = {
Seq(ECDigitalSignature(asm(1).hex))
@ -103,20 +103,27 @@ object P2PKHScriptSignature extends ScriptFactory[P2PKHScriptSignature] {
)
}
/** Builds a P2PKH ScriptSig from a signature and raw ECPublicKeyBytes (may be invalid). */
private[core] def apply(
signature: ECDigitalSignature,
pubKeyBytes: ECPublicKeyBytes): P2PKHScriptSignature = {
val signatureBytesToPushOntoStack =
BitcoinScriptUtil.calculatePushOp(signature.bytes)
val pubKeyBytesToPushOntoStack =
BitcoinScriptUtil.calculatePushOp(pubKeyBytes.bytes)
val asm: Seq[ScriptToken] =
signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.bytes)) ++
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKeyBytes.bytes))
fromAsm(asm)
}
/** Builds a script signature from a digital signature and a public key
* this is a pay to public key hash script sig
*/
def apply(
signature: ECDigitalSignature,
pubKey: ECPublicKey): P2PKHScriptSignature = {
val signatureBytesToPushOntoStack =
BitcoinScriptUtil.calculatePushOp(signature.bytes)
val pubKeyBytesToPushOntoStack =
BitcoinScriptUtil.calculatePushOp(pubKey.bytes)
val asm: Seq[ScriptToken] =
signatureBytesToPushOntoStack ++ Seq(ScriptConstant(signature.hex)) ++
pubKeyBytesToPushOntoStack ++ Seq(ScriptConstant(pubKey.hex))
fromAsm(asm)
P2PKHScriptSignature(signature, pubKey.toPublicKeyBytes())
}
/** Determines if the given asm matches a [[P2PKHScriptSignature]] */
@ -180,11 +187,11 @@ sealed trait P2SHScriptSignature extends ScriptSignature {
}
/** Returns the public keys for the p2sh scriptSignature */
def publicKeys: Seq[ECPublicKey] = {
def publicKeys: Seq[ECPublicKeyBytes] = {
val pubKeys: Seq[ScriptToken] = redeemScript.asm
.filter(_.isInstanceOf[ScriptConstant])
.filterNot(_.isInstanceOf[ScriptNumberOperation])
pubKeys.map(k => ECPublicKey(k.hex))
pubKeys.map(k => ECPublicKeyBytes(k.bytes))
}
/** The digital signatures inside of the scriptSig */

View file

@ -10,6 +10,7 @@ import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{
ECDigitalSignature,
ECPublicKey,
ECPublicKeyBytes,
EmptyDigitalSignature,
Factory,
NetworkElement
@ -43,7 +44,7 @@ sealed abstract class ScriptWitnessV0 extends ScriptWitness {
* Format: <pubKey> <signature>
*/
sealed abstract class P2WPKHWitnessV0 extends ScriptWitnessV0 {
def pubKey: ECPublicKey = ECPublicKey(stack.head)
def pubKey: ECPublicKeyBytes = ECPublicKeyBytes(stack.head)
def signature: ECDigitalSignature =
stack(1) match {
@ -63,14 +64,25 @@ object P2WPKHWitnessV0 {
private def apply(stack: Seq[ByteVector]): P2WPKHWitnessV0 =
P2WPKHWitnessV0Impl(stack)
private[bitcoins] def apply(
pubKeyBytes: ECPublicKeyBytes): P2WPKHWitnessV0 = {
P2WPKHWitnessV0(pubKeyBytes, EmptyDigitalSignature)
}
def apply(pubKey: ECPublicKey): P2WPKHWitnessV0 = {
P2WPKHWitnessV0(pubKey, EmptyDigitalSignature)
}
private[bitcoins] def apply(
publicKeyBytes: ECPublicKeyBytes,
signature: ECDigitalSignature): P2WPKHWitnessV0 = {
P2WPKHWitnessV0(Seq(publicKeyBytes.bytes, signature.bytes))
}
def apply(
publicKey: ECPublicKey,
signature: ECDigitalSignature): P2WPKHWitnessV0 = {
P2WPKHWitnessV0(Seq(publicKey.bytes, signature.bytes))
P2WPKHWitnessV0(publicKey.toPublicKeyBytes(), signature)
}
def fromP2PKHScriptSig(scriptSig: ScriptSignature): P2WPKHWitnessV0 =
@ -176,11 +188,11 @@ object ScriptWitness extends Factory[ScriptWitness] {
if (stack.isEmpty) {
EmptyScriptWitness
} else if (isPubKey && stack.size == 2) {
val pubKey = ECPublicKey(stack.head)
val pubKey = ECPublicKeyBytes(stack.head)
val sig = ECDigitalSignature(stack(1))
P2WPKHWitnessV0(pubKey, sig)
} else if (isPubKey && stack.size == 1) {
val pubKey = ECPublicKey(stack.head)
val pubKey = ECPublicKeyBytes(stack.head)
P2WPKHWitnessV0(pubKey)
} else {
//wont match a Vector if I don't convert to list

View file

@ -503,10 +503,14 @@ case class InputPSBTMap(elements: Vector[InputPSBTRecord])
addLeaves(conditional.trueSPK, path :+ true)
addLeaves(conditional.falseSPK, path :+ false)
case p2pkWithTimeout: P2PKWithTimeoutScriptPubKey =>
addLeaves(P2PKScriptPubKey(p2pkWithTimeout.pubKey), path :+ true)
addLeaves(P2PKScriptPubKey.fromP2PKWithTimeout(p2pkWithTimeout,
timeoutBranch =
false),
path :+ true)
val timeoutSPK = CLTVScriptPubKey(
p2pkWithTimeout.lockTime,
P2PKScriptPubKey(p2pkWithTimeout.timeoutPubKey))
P2PKScriptPubKey.fromP2PKWithTimeout(p2pkWithTimeout,
timeoutBranch = true))
addLeaves(timeoutSPK, path :+ false)
case cltv: CLTVScriptPubKey =>
addLeaves(cltv.nestedScriptPubKey, path)

View file

@ -157,7 +157,7 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
}
case class PartialSignature(
pubKey: ECPublicKey,
pubKey: ECPublicKeyBytes,
signature: ECDigitalSignature)
extends InputPSBTRecord {
require(pubKey.byteSize == 33,
@ -172,6 +172,12 @@ object InputPSBTRecord extends Factory[InputPSBTRecord] {
object PartialSignature extends Factory[PartialSignature] {
def apply(
pubKey: ECPublicKey,
signature: ECDigitalSignature): PartialSignature = {
PartialSignature(pubKey.toPublicKeyBytes(), signature)
}
def dummyPartialSig(
pubKey: ECPublicKey = ECPublicKey.freshPublicKey): PartialSignature = {
PartialSignature(pubKey, DummyECDigitalSignature)

View file

@ -14,7 +14,7 @@ import org.bitcoins.core.util.BitcoinScriptUtil
import org.bitcoins.crypto.{
CryptoUtil,
ECDigitalSignature,
ECPublicKey,
ECPublicKeyBytes,
HashDigest
}
import scodec.bits.ByteVector
@ -77,7 +77,7 @@ sealed abstract class CryptoInterpreter {
if (program.stack.size < 2) {
program.failExecution(ScriptErrorInvalidStackOperation)
} else {
val pubKey = ECPublicKey(program.stack.head.bytes)
val pubKey = ECPublicKeyBytes(program.stack.head.bytes)
val signature = ECDigitalSignature(program.stack.tail.head.bytes)
val flags = program.flags
val restOfStack = program.stack.tail.tail
@ -182,7 +182,8 @@ sealed abstract class CryptoInterpreter {
program.stack.tail
.slice(nPossibleSignatures.toInt, program.stack.tail.size))
val pubKeys = pubKeysScriptTokens.map(key => ECPublicKey(key.bytes))
val pubKeys =
pubKeysScriptTokens.map(key => ECPublicKeyBytes(key.bytes))
//+1 is for the fact that we have the # of sigs + the script token indicating the # of sigs
val signaturesScriptTokens = program.stack.tail.slice(

View file

@ -35,7 +35,7 @@ import org.bitcoins.core.script.{
}
import org.bitcoins.core.serializers.script.ScriptParser
import org.bitcoins.core.wallet.utxo._
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKey}
import org.bitcoins.crypto.{ECDigitalSignature, ECPublicKeyBytes}
import scodec.bits.ByteVector
import scala.annotation.tailrec
@ -308,11 +308,13 @@ trait BitcoinScriptUtil {
* [[https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L202]].
*/
def checkPubKeyEncoding(
key: ECPublicKey,
key: ECPublicKeyBytes,
program: ExecutionInProgressScriptProgram): Boolean =
checkPubKeyEncoding(key, program.flags)
def checkPubKeyEncoding(key: ECPublicKey, flags: Seq[ScriptFlag]): Boolean = {
def checkPubKeyEncoding(
key: ECPublicKeyBytes,
flags: Seq[ScriptFlag]): Boolean = {
if (
ScriptFlagUtil.requireStrictEncoding(flags) &&
!isCompressedOrUncompressedPubKey(key)
@ -325,7 +327,7 @@ trait BitcoinScriptUtil {
* @param key the public key that is being checked
* @return true if the key is compressed/uncompressed otherwise false
*/
def isCompressedOrUncompressedPubKey(key: ECPublicKey): Boolean = {
def isCompressedOrUncompressedPubKey(key: ECPublicKeyBytes): Boolean = {
if (key.bytes.size < 33) {
// Non-canonical public key: too short
return false
@ -345,7 +347,7 @@ trait BitcoinScriptUtil {
}
/** Checks if the given public key is a compressed public key */
def isCompressedPubKey(key: ECPublicKey): Boolean = {
def isCompressedPubKey(key: ECPublicKeyBytes): Boolean = {
(key.bytes.size == 33) && (key.bytes.head == 0x02 || key.bytes.head == 0x03)
}
@ -361,7 +363,7 @@ trait BitcoinScriptUtil {
* [[https://github.com/bitcoin/bitcoin/blob/528472111b4965b1a99c4bcf08ac5ec93d87f10f/src/script/interpreter.cpp#L214-L223]]
*/
def isValidPubKeyEncoding(
pubKey: ECPublicKey,
pubKey: ECPublicKeyBytes,
sigVersion: SignatureVersion,
flags: Seq[ScriptFlag]): Option[ScriptError] = {
if (

View file

@ -256,7 +256,7 @@ object BitcoinSigner extends SignerUtils {
psbt
.inputMaps(inputIndex)
.partialSignatures
.exists(_.pubKey == signer.publicKey)
.exists(_.pubKey.toPublicKey == signer.publicKey)
) {
throw new IllegalArgumentException(
"Input has already been signed with this key")
@ -331,7 +331,7 @@ sealed abstract class RawSingleKeyBitcoinSigner[-InputType <: RawInputInfo]
signSingle(spendingInfo.toSingle(0), unsignedTx, isDummySignature)
val scriptSig =
keyAndSigToScriptSig(partialSignature.pubKey,
keyAndSigToScriptSig(partialSignature.pubKey.toPublicKey,
partialSignature.signature,
spendingInfoToSatisfy)

View file

@ -10,6 +10,7 @@ import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.util.{BitcoinScriptUtil, BytesUtil}
import org.bitcoins.crypto.{
ECPublicKey,
ECPublicKeyBytes,
LowRDummyECDigitalSignature,
NetworkElement,
Sign
@ -352,8 +353,10 @@ object RawInputInfo {
case p2pk: P2PKScriptPubKey =>
P2PKInputInfo(outPoint, amount, p2pk)
case p2pkh: P2PKHScriptPubKey =>
hashPreImages.collectFirst { case pubKey: ECPublicKey =>
pubKey
hashPreImages.collectFirst {
case pubKey: ECPublicKey =>
pubKey
case pubKeyBytes: ECPublicKeyBytes => pubKeyBytes.toPublicKey
} match {
case None =>
throw new IllegalArgumentException(
@ -418,7 +421,8 @@ case class P2PKInputInfo(
override def conditionalPath: ConditionalPath =
ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = Vector(scriptPubKey.publicKey)
override def pubKeys: Vector[ECPublicKey] = Vector(
scriptPubKey.publicKey.toPublicKey)
override def requiredSigs: Int = 1
}
@ -454,7 +458,8 @@ case class P2PKWithTimeoutInputInfo(
}
override def pubKeys: Vector[ECPublicKey] =
Vector(scriptPubKey.pubKey, scriptPubKey.timeoutPubKey)
Vector(scriptPubKey.pubKey.toPublicKey,
scriptPubKey.timeoutPubKey.toPublicKey)
override def requiredSigs: Int = 1
}
@ -468,7 +473,8 @@ case class MultiSignatureInputInfo(
override def conditionalPath: ConditionalPath =
ConditionalPath.NoCondition
override def pubKeys: Vector[ECPublicKey] = scriptPubKey.publicKeys.toVector
override def pubKeys: Vector[ECPublicKey] =
scriptPubKey.publicKeys.map(_.toPublicKey).toVector
override def requiredSigs: Int = scriptPubKey.requiredSigs
}
@ -545,7 +551,7 @@ object SegwitV0NativeInputInfo {
Vector.empty): SegwitV0NativeInputInfo = {
scriptWitness match {
case p2wpkh: P2WPKHWitnessV0 =>
P2WPKHV0InputInfo(outPoint, amount, p2wpkh.pubKey)
P2WPKHV0InputInfo(outPoint, amount, p2wpkh.pubKey.toPublicKey)
case p2wsh: P2WSHWitnessV0 =>
P2WSHV0InputInfo(outPoint,
amount,

View file

@ -27,7 +27,7 @@ class ECPrivateKeyTest extends BitcoinSCryptoTest {
}
it must "not serialize a ECPrivateKey toString" in {
ECPrivateKey().toString must be("Masked(ECPrivateKeyImpl)")
ECPrivateKey().toString must be("Masked(ECPrivateKey)")
}
it must "successfully negate itself" in {

View file

@ -2,8 +2,6 @@ package org.bitcoins.crypto
import scodec.bits._
import scala.concurrent.ExecutionContext
class ECPublicKeyTest extends BitcoinSCryptoTest {
it must "be able to decompress keys" in {
@ -30,17 +28,17 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
it must "decompress keys correctly" in {
forAll(CryptoGenerators.privateKey) { privKey =>
val pubKey = privKey.publicKey
val privKeyBytes = ECPrivateKeyBytes(privKey.bytes)
val pubKey = privKeyBytes.publicKeyBytes
assert(privKey.isCompressed)
assert(privKeyBytes.isCompressed)
assert(pubKey.isCompressed)
val decompressedPrivKey =
ECPrivateKey(privKey.bytes, isCompressed = false)(
ExecutionContext.global)
ECPrivateKeyBytes(privKey.bytes, isCompressed = false)
val decompressedPubKey = pubKey.decompressed
assert(decompressedPrivKey.publicKey == decompressedPubKey)
assert(decompressedPrivKey.publicKeyBytes == decompressedPubKey)
assert(pubKey.bytes.tail == decompressedPubKey.bytes.splitAt(33)._1.tail)
}
}
@ -53,27 +51,25 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
}
it must "be able to compress/decompress public keys" in {
val privkey = ECPrivateKey.freshPrivateKey
val privkey = ECPrivateKeyBytes.freshPrivateKey
assert(CryptoUtil.secKeyVerify(privkey.bytes))
assert(privkey.isCompressed)
val notCompressedKey =
ECPrivateKey(bytes = privkey.bytes, isCompressed = false)
val pubkey = CryptoUtil.toPublicKey(notCompressedKey)
ECPrivateKeyBytes(bytes = privkey.bytes, isCompressed = false)
val pubkey = notCompressedKey.publicKeyBytes
assert(CryptoUtil.isValidPubKey(pubkey.bytes))
assert(!pubkey.isCompressed)
val compressed = privkey.publicKey
val compressed = privkey.publicKeyBytes
assert(CryptoUtil.isValidPubKey(compressed.bytes))
assert(compressed.isCompressed)
val converted =
CryptoUtil.publicKeyConvert(pubkey, compressed = true)
val converted = pubkey.compressed
assert(CryptoUtil.isValidPubKey(converted.bytes))
assert(converted.isCompressed)
val decompressed =
CryptoUtil.publicKeyConvert(compressed, compressed = false)
val decompressed = compressed.decompressed
assert(CryptoUtil.isValidPubKey(decompressed.bytes))
assert(!decompressed.isCompressed)
@ -84,8 +80,7 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
}
it must "not be able to add opposite public keys" in {
val privkey = ECPrivateKey.freshPrivateKey
val pubkey1 = privkey.publicKey
val pubkey1 = ECPublicKey.freshPublicKey
val firstByte: Byte =
if (pubkey1.bytes.head == 0x02) 0x03
else if (pubkey1.bytes.head == 0x03) 0x02
@ -93,21 +88,37 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
val pubkey2 =
ECPublicKey.fromBytes(ByteVector(firstByte) ++ pubkey1.bytes.tail)
assertThrows[Exception] {
val sumKey = CryptoUtil.add(pubkey1, pubkey2)
if (sumKey == ECPublicKey.infinity) fail()
}
assertThrows[Exception](CryptoUtil.add(pubkey1, pubkey2))
assertThrows[Exception](
CryptoUtil.add(pubkey1.compressed, pubkey2.compressed))
assertThrows[Exception](
CryptoUtil.add(pubkey1.decompressed, pubkey2.decompressed))
}
val decompressedPubkey1 =
CryptoUtil.publicKeyConvert(pubkey1, compressed = false)
it must "be able to add multiple public keys together with sub-sums of 0x00" in {
val pubkey1 = ECPublicKey.freshPublicKey
val firstByte: Byte =
if (pubkey1.bytes.head == 0x02) 0x03
else if (pubkey1.bytes.head == 0x03) 0x02
else pubkey1.bytes.head
val pubkey2 =
ECPublicKey.fromBytes(ByteVector(firstByte) ++ pubkey1.bytes.tail)
val pubkey3 = ECPublicKey.freshPublicKey
val firstByte2: Byte =
if (pubkey3.bytes.head == 0x02) 0x03
else if (pubkey3.bytes.head == 0x03) 0x02
else pubkey3.bytes.head
val pubkey4 =
ECPublicKey.fromBytes(ByteVector(firstByte2) ++ pubkey3.bytes.tail)
val decompressedPubkey2 =
CryptoUtil.publicKeyConvert(pubkey2, compressed = false)
assertThrows[Exception] {
val sumKey = CryptoUtil.add(decompressedPubkey1, decompressedPubkey2)
if (sumKey == ECPublicKey.infinity) fail()
}
assert(
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey2, pubkey3)) == pubkey3)
assert(
CryptoUtil.combinePubKeys(Vector(pubkey3, pubkey1, pubkey2)) == pubkey3)
assertThrows[Exception](
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey2, pubkey3, pubkey4)))
assertThrows[Exception](
CryptoUtil.combinePubKeys(Vector(pubkey1, pubkey3, pubkey2, pubkey4)))
}
it must "correctly compress keys" in {
@ -116,16 +127,22 @@ class ECPublicKeyTest extends BitcoinSCryptoTest {
val pubKeyCompressed = pubKey.compressed
val pubKeyDecompressed = pubKey.decompressed
if (privKey.isCompressed) {
assert(pubKey == pubKeyCompressed)
} else {
assert(pubKey == pubKeyDecompressed)
}
assert(!pubKey.isCompressed)
assert(pubKeyCompressed.isFullyValid)
assert(pubKeyDecompressed.isFullyValid)
assert(pubKeyCompressed.decompressed == pubKeyDecompressed)
assert(pubKeyCompressed.compressed == pubKeyCompressed)
assert(pubKeyDecompressed.compressed == pubKeyCompressed)
assert(pubKeyDecompressed.decompressed == pubKeyDecompressed)
assert(pubKeyCompressed == pubKeyDecompressed)
assert(!pubKeyCompressed.decompressed.isCompressed)
assert(pubKeyCompressed.compressed.isCompressed)
assert(pubKeyDecompressed.compressed.isCompressed)
assert(!pubKeyDecompressed.decompressed.isCompressed)
assert(pubKeyCompressed == pubKey)
assert(pubKeyDecompressed == pubKey)
assert(
pubKeyCompressed.bytes.tail == pubKeyDecompressed.decompressedBytes
.splitAt(33)
._1
.tail)
}
}

View file

@ -63,8 +63,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def toPublicKey(privateKey: ECPrivateKey): ECPublicKey = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val pubKeyBuffer =
SECP256k1.publicKeyCreate(key = buffer,
compressed = privateKey.isCompressed)
SECP256k1.publicKeyCreate(key = buffer, compressed = false)
val privKeyByteVec = CryptoJsUtil.toByteVector(pubKeyBuffer)
ECPublicKey.fromBytes(privKeyByteVec)
}
@ -151,8 +150,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val bufferPubKey =
SECP256k1.publicKeyCreate(buffer, privateKey.isCompressed)
val bufferPubKey = SECP256k1.publicKeyCreate(buffer, compressed = false)
val byteVec = CryptoJsUtil.toByteVector(bufferPubKey)
ECPublicKey.fromBytes(byteVec)
}
@ -178,7 +176,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
}
override def verify(
publicKey: ECPublicKey,
publicKey: PublicKey[_],
data: ByteVector,
signature: ECDigitalSignature): Boolean = {
val dataBuffer = CryptoJsUtil.toNodeBuffer(data)
@ -190,7 +188,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.bytes)
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.decompressedBytes)
val tweakBuffer = CryptoJsUtil.toNodeBuffer(tweak.bytes)
val keyBuffer =
SECP256k1.publicKeyTweakMul(pubKeyBuffer, tweakBuffer, compress = true)
@ -198,23 +196,9 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
ECPublicKey.fromBytes(keyByteVec)
}
def publicKeyConvert(buffer: ByteVector, compressed: Boolean): ByteVector = {
val pubKeyBuffer =
publicKeyConvert(CryptoJsUtil.toNodeBuffer(buffer), compressed)
CryptoJsUtil.toByteVector(pubKeyBuffer)
}
def publicKeyConvert(buffer: Buffer, compressed: Boolean): Buffer =
SECP256k1.publicKeyConvert(buffer, compressed)
override def publicKeyConvert(
key: ECPublicKey,
compressed: Boolean): ECPublicKey =
ECPublicKey(publicKeyConvert(key.bytes, compressed))
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
val pk1Buffer = CryptoJsUtil.toNodeBuffer(pk1.bytes)
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.bytes)
val pk1Buffer = CryptoJsUtil.toNodeBuffer(pk1.decompressedBytes)
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.decompressedBytes)
try {
val keyBuffer =
SECP256k1.publicKeyCombine(js.Array(pk1Buffer, pk2Buffer),
@ -223,21 +207,13 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
ECPublicKey.fromBytes(keyBytes)
} catch {
case ex: JavaScriptException =>
val k1: ByteVector = pk1.bytes
val k2: ByteVector = pk2.bytes
// check for infinity
val k1: ByteVector =
if (pk1.isCompressed) pk1.bytes
else publicKeyConvert(pk1.bytes, compressed = true)
val k2: ByteVector =
if (pk2.isCompressed) pk2.bytes
else publicKeyConvert(pk2.bytes, compressed = true)
if (
((k1.head == 0x02 && k2.head == 0x03) ||
(k1.head == 0x03 && k2.head == 0x02)) &&
k1.tail == k2.tail
) {
ECPublicKey.infinity
if ((k1.head ^ k2.head) == 0x01 && k1.tail == k2.tail) {
throw new IllegalArgumentException(
s"Invalid public key sum, got 0x00 = $pk1 + $pk2")
} else {
throw ex
}
@ -247,7 +223,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.bytes)
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.decompressedBytes)
val privKeyBuffer = CryptoJsUtil.toNodeBuffer(privkey.bytes)
val keyBuffer =
SECP256k1.publicKeyTweakAdd(pubKeyBuffer, privKeyBuffer, compress = true)
@ -269,19 +245,19 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
hi | lo
}
override def decodePoint(bytes: ByteVector): ECPoint = {
override def decodePoint(bytes: ByteVector): SecpPoint = {
if (bytes.size == 1 && bytes(0) == 0x00) {
ECPointInfinity
SecpPointInfinity
} else {
val decoded = SECP256k1.curve
.applyDynamic("decodePoint")(CryptoJsUtil.toNodeBuffer(bytes))
.asInstanceOf[Point]
if (decoded.isInfinity())
ECPointInfinity
SecpPointInfinity
else
ECPoint(new BigInteger(decoded.getX().toString()),
new BigInteger(decoded.getY().toString()))
SecpPoint(new BigInteger(decoded.getX().toString()),
new BigInteger(decoded.getY().toString()))
}
}

View file

@ -6,7 +6,7 @@ import org.bouncycastle.crypto.params.{
ECPublicKeyParameters
}
import org.bouncycastle.crypto.signers.{ECDSASigner, HMacDSAKCalculator}
import org.bouncycastle.math.ec.ECCurve
import org.bouncycastle.math.ec.{ECCurve, ECPoint}
import scodec.bits.ByteVector
import java.math.BigInteger
@ -26,25 +26,23 @@ object BouncyCastleUtil {
decodePubKey(point, publicKey.isCompressed)
}
private[crypto] def decodePoint(
bytes: ByteVector): org.bouncycastle.math.ec.ECPoint = {
private[crypto] def decodePoint(bytes: ByteVector): ECPoint = {
curve.decodePoint(bytes.toArray)
}
private[crypto] def decodePoint(
pubKey: ECPublicKey): org.bouncycastle.math.ec.ECPoint = {
decodePoint(pubKey.bytes)
private[crypto] def decodePoint(pubKey: ECPublicKey): ECPoint = {
decodePoint(pubKey.decompressedBytes)
}
private[crypto] def decodePubKey(
point: org.bouncycastle.math.ec.ECPoint,
point: ECPoint,
isCompressed: Boolean = true): ECPublicKey = {
val bytes = point.getEncoded(isCompressed)
ECPublicKey.fromBytes(ByteVector(bytes))
}
def validatePublicKey(bytes: ByteVector): Boolean = {
Try(decodePoint(bytes))
bytes != ByteVector(0x00) && Try(decodePoint(bytes))
.map(_.getCurve == curve)
.getOrElse(false)
}
@ -68,7 +66,7 @@ object BouncyCastleUtil {
def computePublicKey(privateKey: ECPrivateKey): ECPublicKey = {
val priv = getBigInteger(privateKey.bytes)
val point = G.multiply(priv)
val pubBytes = ByteVector(point.getEncoded(privateKey.isCompressed))
val pubBytes = ByteVector(point.getEncoded(false))
require(
ECPublicKey.isFullyValid(pubBytes),
s"Bouncy Castle failed to generate a valid public key, got: ${CryptoBytesUtil
@ -149,7 +147,7 @@ object BouncyCastleUtil {
def verifyDigitalSignature(
data: ByteVector,
publicKey: ECPublicKey,
publicKey: PublicKey[_],
signature: ECDigitalSignature): Boolean = {
val resultTry = Try {
val publicKeyParams =

View file

@ -1,6 +1,5 @@
package org.bitcoins.crypto
import org.bitcoins.crypto
import org.bouncycastle.crypto.AsymmetricCipherKeyPair
import org.bouncycastle.crypto.digests.{RIPEMD160Digest, SHA512Digest}
import org.bouncycastle.crypto.generators.ECKeyPairGenerator
@ -10,6 +9,7 @@ import org.bouncycastle.crypto.params.{
ECPrivateKeyParameters,
KeyParameter
}
import org.bouncycastle.math.ec.ECPoint
import scodec.bits.ByteVector
import java.math.BigInteger
@ -34,16 +34,14 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
keypair.getPrivate.asInstanceOf[ECPrivateKeyParameters]
val priv: BigInteger = privParams.getD
val bytes = ByteVector(priv.toByteArray)
ECPrivateKey.fromBytes(bytes)
ECPrivateKey.fromBytes(bytes.padLeft(33))
}
/** @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): (
org.bouncycastle.math.ec.ECPoint,
org.bouncycastle.math.ec.ECPoint) = {
def recoverPoint(x: BigInteger): (ECPoint, ECPoint) = {
val bytes = ByteVector(x.toByteArray)
val bytes32 = if (bytes.length < 32) {
@ -148,7 +146,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
}
override def verify(
publicKey: ECPublicKey,
publicKey: PublicKey[_],
data: ByteVector,
signature: ECDigitalSignature): Boolean =
BouncyCastleUtil.verifyDigitalSignature(data, publicKey, signature)
@ -156,11 +154,6 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
BouncyCastleUtil.computePublicKey(privateKey)
override def publicKeyConvert(
key: ECPublicKey,
compressed: Boolean): ECPublicKey =
BouncyCastleUtil.publicKeyConvert(key, compressed)
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement): ECPublicKey =
@ -200,14 +193,14 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
sh.doFinal()
}
override def decodePoint(bytes: ByteVector): crypto.ECPoint = {
override def decodePoint(bytes: ByteVector): SecpPoint = {
val decoded = BouncyCastleUtil.decodePoint(bytes)
if (decoded.isInfinity)
crypto.ECPointInfinity
SecpPointInfinity
else
crypto.ECPoint(decoded.getRawXCoord.getEncoded,
decoded.getRawYCoord.getEncoded)
SecpPoint(decoded.getRawXCoord.getEncoded,
decoded.getRawYCoord.getEncoded)
}
override def pbkdf2WithSha512(

View file

@ -44,8 +44,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def toPublicKey(privateKey: ECPrivateKey): ECPublicKey = {
val pubKeyBytes: Array[Byte] =
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
privateKey.isCompressed)
NativeSecp256k1.computePubkey(privateKey.bytes.toArray, false)
val pubBytes = ByteVector(pubKeyBytes)
require(
ECPublicKey.isFullyValid(pubBytes),
@ -77,7 +76,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
NativeSecp256k1.secKeyVerify(privateKeyBytes.toArray)
override def verify(
publicKey: ECPublicKey,
publicKey: PublicKey[_],
data: ByteVector,
signature: ECDigitalSignature): Boolean = {
val result =
@ -96,17 +95,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
} else result
}
override def decompressed(publicKey: ECPublicKey): ECPublicKey = {
override def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK = {
if (publicKey.isCompressed) {
val decompressed = NativeSecp256k1.decompress(publicKey.bytes.toArray)
ECPublicKey.fromBytes(ByteVector(decompressed))
publicKey.fromBytes(ByteVector(decompressed))
} else publicKey
}
override def publicKey(privateKey: ECPrivateKey): ECPublicKey = {
val pubKeyBytes: Array[Byte] =
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
privateKey.isCompressed)
NativeSecp256k1.computePubkey(privateKey.bytes.toArray, false)
val pubBytes = ByteVector(pubKeyBytes)
require(
ECPublicKey.isFullyValid(pubBytes),
@ -115,17 +113,13 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
ECPublicKey(pubBytes)
}
override def publicKeyConvert(
key: ECPublicKey,
compressed: Boolean): ECPublicKey =
BouncycastleCryptoRuntime.publicKeyConvert(key, compressed)
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement): ECPublicKey = {
val mulBytes = NativeSecp256k1.pubKeyTweakMul(publicKey.bytes.toArray,
tweak.bytes.toArray,
publicKey.isCompressed)
val mulBytes = NativeSecp256k1.pubKeyTweakMul(
publicKey.decompressedBytes.toArray,
tweak.bytes.toArray,
false)
ECPublicKey(ByteVector(mulBytes))
}
@ -142,8 +136,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
}
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
val summands = Array(pk1.bytes.toArray, pk2.bytes.toArray)
val sumKey = NativeSecp256k1.pubKeyCombine(summands, pk1.isCompressed)
val summands =
Array(pk1.decompressedBytes.toArray, pk2.decompressedBytes.toArray)
val sumKey = NativeSecp256k1.pubKeyCombine(summands, false)
ECPublicKey(ByteVector(sumKey))
}
override def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey = {
val summands = pubKeys.map(_.decompressedBytes.toArray).toArray
val sumKey = NativeSecp256k1.pubKeyCombine(summands, false)
ECPublicKey(ByteVector(sumKey))
}
@ -151,9 +153,10 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey): ECPublicKey = {
val tweaked = NativeSecp256k1.pubKeyTweakAdd(pubkey.bytes.toArray,
privkey.bytes.toArray,
privkey.isCompressed)
val tweaked = NativeSecp256k1.pubKeyTweakAdd(
pubkey.decompressedBytes.toArray,
privkey.bytes.toArray,
false)
ECPublicKey(ByteVector(tweaked))
}
@ -214,10 +217,11 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector): ECAdaptorSignature = {
val sig = NativeSecp256k1.adaptorSign(key.bytes.toArray,
adaptorPoint.bytes.toArray,
msg.toArray,
auxRand.toArray)
val sig = NativeSecp256k1.adaptorSign(
key.bytes.toArray,
adaptorPoint.decompressedBytes.toArray,
msg.toArray,
auxRand.toArray)
ECAdaptorSignature(ByteVector(sig))
}
@ -237,7 +241,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
val secretBytes = NativeSecp256k1.adaptorExtractSecret(
signature.bytes.toArray,
adaptorSignature.bytes.toArray,
key.bytes.toArray)
key.decompressedBytes.toArray)
ECPrivateKey(ByteVector(secretBytes))
}
@ -248,9 +252,9 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
msg: ByteVector,
adaptorPoint: ECPublicKey): Boolean = {
NativeSecp256k1.adaptorVerify(adaptorSignature.bytes.toArray,
key.bytes.toArray,
key.decompressedBytes.toArray,
msg.toArray,
adaptorPoint.bytes.toArray)
adaptorPoint.decompressedBytes.toArray)
}
override def isValidSignatureEncoding(
@ -263,16 +267,16 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def sipHash(item: ByteVector, key: SipHashKey): Long =
BouncycastleCryptoRuntime.sipHash(item, key)
override def decodePoint(bytes: ByteVector): ECPoint = {
override def decodePoint(bytes: ByteVector): SecpPoint = {
val infinityPt: ByteVector = ByteVector.fromByte(0x00)
if (bytes == infinityPt) {
ECPointInfinity
SecpPointInfinity
} else {
val pointBytes = NativeSecp256k1.decompress(bytes.toArray)
val xBytes = pointBytes.tail.take(32)
val yBytes = pointBytes.takeRight(32)
ECPoint(xBytes, yBytes)
SecpPoint(xBytes, yBytes)
}
}

View file

@ -35,7 +35,7 @@ object AdaptorUtil {
val randHash = CryptoUtil.sha256ECDSAAdaptorAux(auxRand).bytes
val maskedKey = randHash.xor(privKey.bytes)
val bytesToHash = maskedKey ++ adaptorPoint.compressed.bytes ++ message
val bytesToHash = maskedKey ++ adaptorPoint.bytes ++ message
val nonceHash = algoName match {
case "DLEQ" => CryptoUtil.sha256DLEQ(bytesToHash)
case "ECDSAadaptor/non" => CryptoUtil.sha256ECDSAAdaptorNonce(bytesToHash)
@ -55,10 +55,10 @@ object AdaptorUtil {
r: ECPublicKey,
privateKey: ECPrivateKey): FieldElement = {
CryptoUtil.decodePoint(r) match {
case ECPointInfinity =>
case SecpPointInfinity =>
throw new IllegalArgumentException(
s"Invalid point, got=$ECPointInfinity")
case point: ECPointImpl =>
s"Invalid point, got=$SecpPointInfinity")
case point: SecpPointFinite =>
val rx = FieldElement(point.x.toBigInteger)
val x = privateKey.fieldElement
val m = FieldElement(dataToSign)
@ -106,7 +106,7 @@ object AdaptorUtil {
val untweakedPoint =
m.getPublicKey.add(pubKey.tweakMultiply(rx)).tweakMultiply(s.inverse)
FieldElement(untweakedPoint.compressed.bytes.tail)
FieldElement(untweakedPoint.bytes.tail)
}
/** https://github.com/discreetlogcontracts/dlcspecs/blob/d01595b70269d4204b05510d19bba6a4f4fcff23/ECDSA-adaptor.md#encryption-verification */
@ -163,9 +163,8 @@ object AdaptorUtil {
val secretOrNeg = adaptorSig.adaptedS.multInv(FieldElement(sig.s))
require(
secretOrNeg.getPublicKey.compressed.bytes.tail == adaptor.compressed.bytes.tail,
s"Invalid inputs: $sig, $adaptorSig, and $adaptor")
require(secretOrNeg.getPublicKey.bytes.tail == adaptor.bytes.tail,
s"Invalid inputs: $sig, $adaptorSig, and $adaptor")
if (secretOrNeg.getPublicKey == adaptor) {
secretOrNeg.toPrivateKey

View file

@ -170,11 +170,6 @@ trait CryptoRuntime {
def publicKey(privateKey: ECPrivateKey): ECPublicKey
/** Converts the given public key from its current representation to compressed/not compressed
* depending upon how [[compressed]] is set
*/
def publicKeyConvert(key: ECPublicKey, compressed: Boolean): ECPublicKey
def sign(privateKey: ECPrivateKey, dataToSign: ByteVector): ECDigitalSignature
def signWithEntropy(
@ -185,20 +180,20 @@ trait CryptoRuntime {
def secKeyVerify(privateKeybytes: ByteVector): Boolean
def verify(
publicKey: ECPublicKey,
publicKey: PublicKey[_],
data: ByteVector,
signature: ECDigitalSignature): Boolean
def decompressed(publicKey: ECPublicKey): ECPublicKey = {
def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK = {
if (publicKey.isCompressed) {
decodePoint(publicKey.bytes) match {
case ECPointInfinity => ECPublicKey.fromHex("00")
case point: ECPointImpl =>
case SecpPointInfinity => publicKey.fromHex("00")
case point: SecpPointFinite =>
val decompressedBytes =
ByteVector.fromHex("04").get ++
point.x.bytes ++
point.y.bytes
ECPublicKey(decompressedBytes)
publicKey.fromBytes(decompressedBytes)
}
} else publicKey
}
@ -213,16 +208,50 @@ trait CryptoRuntime {
sum.bytes
}
/** Adds two SecpPoints together and correctly handles the point at infinity (0x00). */
def add(point1: SecpPoint, point2: SecpPoint): SecpPoint = {
(point1, point2) match {
case (SecpPointInfinity, p) => p
case (p, SecpPointInfinity) => p
case (p1: SecpPointFinite, p2: SecpPointFinite) =>
val pk1 = p1.toPublicKey
val pk2 = p2.toPublicKey
if (
(pk1.bytes.head ^ pk2.bytes.head) == 0x01 && pk1.bytes.tail == pk2.bytes.tail
) {
SecpPointInfinity
} else {
add(pk1, pk2).toPoint
}
}
}
/** Adds two public keys together, failing if the sum is 0x00 (the point at infinity). */
def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey
/** Adds a Vector of public keys together, failing only if the total sum is 0x00
* (the point at infinity), but still succeeding if sub-sums are 0x00.
*/
def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey = {
val summandPoints = pubKeys.map(_.toPoint)
val sumPoint = summandPoints.reduce[SecpPoint](add(_, _))
sumPoint match {
case SecpPointInfinity =>
throw new IllegalArgumentException(
"Sum result was 0x00, an invalid public key.")
case p: SecpPointFinite => p.toPublicKey
}
}
def pubKeyTweakAdd(pubkey: ECPublicKey, privkey: ECPrivateKey): ECPublicKey
def isValidPubKey(bytes: ByteVector): Boolean
def decodePoint(bytes: ByteVector): ECPoint
def decodePoint(bytes: ByteVector): SecpPoint
def decodePoint(pubKey: ECPublicKey): ECPoint = {
decodePoint(pubKey.bytes)
def decodePoint(pubKey: ECPublicKey): SecpPoint = {
decodePoint(pubKey.decompressedBytes)
}
def schnorrSign(
@ -268,11 +297,12 @@ trait CryptoRuntime {
val sigPoint = s.publicKey
val challengePoint = schnorrPubKey.publicKey.tweakMultiply(negE)
val computedR = challengePoint.add(sigPoint)
decodePoint(computedR) match {
case ECPointInfinity => false
case ECPointImpl(_, yCoord) =>
!yCoord.toBigInteger.testBit(0) && computedR.schnorrNonce == rx
val computedR = challengePoint.toPoint.add(sigPoint.toPoint)
computedR match {
case SecpPointInfinity => false
case point: SecpPointFinite =>
!point.y.toBigInteger.testBit(
0) && point.toPublicKey.schnorrNonce == rx
}
case Failure(_) => false
}

View file

@ -83,11 +83,6 @@ trait CryptoUtil extends CryptoRuntime {
override def publicKey(privateKey: ECPrivateKey): ECPublicKey =
cryptoRuntime.publicKey(privateKey)
override def publicKeyConvert(
key: ECPublicKey,
compressed: Boolean): ECPublicKey =
cryptoRuntime.publicKeyConvert(key, compressed)
override def sign(
privateKey: ECPrivateKey,
dataToSign: ByteVector): ECDigitalSignature =
@ -103,12 +98,12 @@ trait CryptoUtil extends CryptoRuntime {
cryptoRuntime.secKeyVerify(privateKeybytes)
override def verify(
publicKey: ECPublicKey,
publicKey: PublicKey[_],
data: ByteVector,
signature: ECDigitalSignature): Boolean =
cryptoRuntime.verify(publicKey, data, signature)
override def decompressed(publicKey: ECPublicKey): ECPublicKey =
override def decompressed[PK <: PublicKey[PK]](publicKey: PK): PK =
cryptoRuntime.decompressed(publicKey)
override def tweakMultiply(
@ -122,9 +117,15 @@ trait CryptoUtil extends CryptoRuntime {
override def add(pk1: ByteVector, pk2: ECPrivateKey): ByteVector =
cryptoRuntime.add(pk1, pk2)
override def add(point1: SecpPoint, point2: SecpPoint): SecpPoint =
cryptoRuntime.add(point1, point2)
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey =
cryptoRuntime.add(pk1, pk2)
override def combinePubKeys(pubKeys: Vector[ECPublicKey]): ECPublicKey =
cryptoRuntime.combinePubKeys(pubKeys)
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey): ECPublicKey =
@ -197,7 +198,7 @@ trait CryptoUtil extends CryptoRuntime {
override def sipHash(item: ByteVector, key: SipHashKey): Long =
cryptoRuntime.sipHash(item, key)
override def decodePoint(bytes: ByteVector): ECPoint =
override def decodePoint(bytes: ByteVector): SecpPoint =
cryptoRuntime.decodePoint(bytes)
override def randomBytes(n: Int): ByteVector = cryptoRuntime.randomBytes(n)

View file

@ -45,7 +45,7 @@ object DLEQUtil {
tweakedPoint: ECPublicKey,
auxRand: ByteVector): FieldElement = {
val hash = CryptoUtil
.sha256(point.compressed.bytes ++ tweakedPoint.compressed.bytes)
.sha256(point.bytes ++ tweakedPoint.bytes)
.bytes
AdaptorUtil.adaptorNonce(hash,
@ -66,8 +66,8 @@ object DLEQUtil {
p2: ECPublicKey): ByteVector = {
CryptoUtil
.sha256DLEQ(
p1.compressed.bytes ++ adaptorPoint.compressed.bytes ++
p2.compressed.bytes ++ r1.compressed.bytes ++ r2.compressed.bytes)
p1.bytes ++ adaptorPoint.bytes ++
p2.bytes ++ r1.bytes ++ r2.bytes)
.bytes
}

View file

@ -38,7 +38,7 @@ object ECAdaptorSignature extends Factory[ECAdaptorSignature] {
dleqProofE: FieldElement,
dleqProofS: FieldElement): ECAdaptorSignature = {
fromBytes(
tweakedNonce.compressed.bytes ++ untweakedNonce.compressed.bytes ++
tweakedNonce.bytes ++ untweakedNonce.bytes ++
adaptedS.bytes ++ dleqProofE.bytes ++ dleqProofS.bytes
)
}

View file

@ -3,19 +3,143 @@ package org.bitcoins.crypto
import scodec.bits.ByteVector
import java.math.BigInteger
import scala.annotation.tailrec
import scala.concurrent.ExecutionContext
/** Represents the raw bytes which are meant to represent an ECKey without deserializing. */
sealed trait ECKeyBytes extends NetworkElement
/** Represents a serialization sensitive ECPrivateKey (such as is used in WIF). */
case class ECPrivateKeyBytes(bytes: ByteVector, isCompressed: Boolean = true)
extends ECKeyBytes
with MaskedToString {
val toPrivateKey: ECPrivateKey = ECPrivateKey(bytes)
/** Returns the raw ECPublicKeyBytes serialized using isCompressed. */
def publicKeyBytes: ECPublicKeyBytes = {
val pubKey = toPrivateKey.publicKey
if (isCompressed) {
ECPublicKeyBytes(pubKey.bytes)
} else {
ECPublicKeyBytes(pubKey.decompressedBytes)
}
}
override def toStringSensitive: String = s"ECPrivateKeyBytes($hex)"
}
object ECPrivateKeyBytes extends Factory[ECPrivateKeyBytes] {
override def fromBytes(bytes: ByteVector): ECPrivateKeyBytes = {
val modifiedBytes = ECPrivateKey.fromBytes(bytes).bytes
new ECPrivateKeyBytes(modifiedBytes)
}
def freshPrivateKey(isCompressed: Boolean): ECPrivateKeyBytes = {
CryptoUtil.freshPrivateKey.toPrivateKeyBytes(isCompressed)
}
def freshPrivateKey: ECPrivateKeyBytes = {
CryptoUtil.freshPrivateKey.toPrivateKeyBytes()
}
}
/** Represents any type which wraps public key bytes which can be used for ECDSA verification.
* Should always be instantiated with class X extends PublicKey[X].
*/
sealed trait PublicKey[PK <: PublicKey[PK]] extends NetworkElement {
/** The fromBytes function for the PK type. */
private[crypto] def fromBytes(bytes: ByteVector): PK
private[crypto] def fromHex(hex: String): PK = {
fromBytes(CryptoBytesUtil.decodeHex(hex))
}
/** Returns this but as a PK. */
private def thisAsPK: PK = {
this.asInstanceOf[PK]
}
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean =
verify(hash.bytes, signature)
/** Verifies if a given piece of data is signed by the
* [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]]'s corresponding
* [[org.bitcoins.crypto.ECPublicKey ECPublicKey]].
*/
def verify(data: ByteVector, signature: ECDigitalSignature): Boolean = {
CryptoUtil.verify(this, data, signature)
}
def verify(hex: String, signature: ECDigitalSignature): Boolean =
verify(CryptoBytesUtil.decodeHex(hex), signature)
/** Returns true if the underlying bytes being wrapped are compressed */
def isCompressed: Boolean = bytes.size == 33
/** Returns true if the underlying bytes being wrapped are valid according to secp256k1 */
def isFullyValid: Boolean = ECPublicKey.isFullyValid(bytes)
/** Returns the decompressed version of this PublicKey */
lazy val decompressed: PK = {
if (isCompressed) {
CryptoUtil.decompressed(thisAsPK)
} else thisAsPK
}
/** Returns the compressed version of this PublicKey */
lazy val compressed: PK = {
if (isCompressed || bytes == ByteVector.fromByte(0x00)) {
thisAsPK
} else {
val key = if (bytes.length == 65) this else decompressed
val (x, y) = key.bytes.tail.splitAt(32)
val leadByte = if (FieldElement(y).isEven) 2.toByte else 3.toByte
fromBytes(x.+:(leadByte))
}
}
}
/** Wraps raw ECPublicKey bytes without doing any validation or deserialization (may be invalid). */
case class ECPublicKeyBytes(bytes: ByteVector)
extends ECKeyBytes
with PublicKey[ECPublicKeyBytes] {
/** Parse these bytes into the bitcoin-s internal public key type. */
def toPublicKey: ECPublicKey = ECPublicKey(bytes)
override private[crypto] def fromBytes(bytes: ByteVector): ECPublicKeyBytes =
ECPublicKeyBytes(bytes)
}
object ECPublicKeyBytes extends Factory[ECPublicKeyBytes] {
override def fromBytes(bytes: ByteVector): ECPublicKeyBytes = {
new ECPublicKeyBytes(bytes)
}
def freshPublicKey: ECPublicKeyBytes = {
ECPrivateKeyBytes.freshPrivateKey.publicKeyBytes
}
}
/** Created by chris on 2/16/16.
* Represents a fully parsed and validated ECDSA private or public key.
*/
sealed abstract class BaseECKey extends NetworkElement
/** Created by chris on 2/16/16.
* A valid deserialized private key.
*
* Note that there is no notion of compressed vs. decompressed
* as there is in Wallet Import Format (WIF), if dealing with
* external wallets then ECPrivateKeyBytes may be needed.
*/
sealed abstract class ECPrivateKey
case class ECPrivateKey(bytes: ByteVector)
extends BaseECKey
with AdaptorSign
with MaskedToString {
require(CryptoUtil.secKeyVerify(bytes), s"Invalid key, hex: ${bytes.toHex}")
/** Signs a given sequence of bytes with the signingKey
* @param dataToSign the bytes to be signed
@ -91,16 +215,13 @@ sealed abstract class ECPrivateKey
def negate: ECPrivateKey = {
val negPrivKeyNum = N.subtract(new BigInteger(1, bytes.toArray))
ECPrivateKey(ByteVector(negPrivKeyNum.toByteArray))
ECPrivateKey(ByteVector(negPrivKeyNum.toByteArray).padLeft(33))
}
def add(other: ECPrivateKey): ECPrivateKey = {
CryptoUtil.add(this, other)
}
/** Signifies if the this private key corresponds to a compressed public key */
def isCompressed: Boolean
/** Derives the public for a the private key */
override def publicKey: ECPublicKey =
CryptoUtil.publicKey(this)
@ -115,83 +236,61 @@ sealed abstract class ECPrivateKey
def fieldElement: FieldElement = FieldElement(bytes)
override def toStringSensitive: String = s"ECPrivateKey($hex,$isCompressed)"
override def toStringSensitive: String = s"ECPrivateKey($hex)"
def toPrivateKeyBytes(isCompressed: Boolean = true): ECPrivateKeyBytes = {
ECPrivateKeyBytes(bytes, isCompressed)
}
}
object ECPrivateKey extends Factory[ECPrivateKey] {
private case class ECPrivateKeyImpl(
override val bytes: ByteVector,
isCompressed: Boolean,
ec: ExecutionContext)
extends ECPrivateKey {
require(CryptoUtil.secKeyVerify(bytes), s"Invalid key, hex: ${bytes.toHex}")
}
def apply(bytes: ByteVector, isCompressed: Boolean)(implicit
ec: ExecutionContext): ECPrivateKey = {
ECPrivateKeyImpl(bytes, isCompressed, ec)
}
override def fromBytes(bytes: ByteVector): ECPrivateKey =
fromBytes(bytes, isCompressed = true)
@tailrec
def fromBytes(bytes: ByteVector, isCompressed: Boolean): ECPrivateKey = {
override def fromBytes(bytes: ByteVector): ECPrivateKey = {
if (bytes.size == 32)
ECPrivateKeyImpl(bytes, isCompressed, ExecutionContext.global)
else if (bytes.size < 32) {
//means we need to pad the private key with 0 bytes so we have 32 bytes
ECPrivateKey.fromBytes(bytes.padLeft(32), isCompressed)
} //this is for the case when java serialies a BigInteger to 33 bytes to hold the signed num representation
else if (bytes.size == 33)
ECPrivateKey.fromBytes(bytes.slice(1, 33), isCompressed)
else
new ECPrivateKey(bytes)
else if (bytes.size == 33 && bytes.head == 0x00) {
new ECPrivateKey(bytes.slice(1, 33))
} else
throw new IllegalArgumentException(
"Private keys cannot be greater than 33 bytes in size, got: " +
"Private keys must be 32 in size, got: " +
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
def fromHex(hex: String, isCompressed: Boolean): ECPrivateKey =
fromBytes(CryptoBytesUtil.decodeHex(hex), isCompressed)
def fromFieldElement(fieldElement: FieldElement): ECPrivateKey = {
fieldElement.toPrivateKey
}
/** Generates a fresh [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] that has not been used before. */
def apply(): ECPrivateKey = ECPrivateKey(true)
def apply(isCompressed: Boolean): ECPrivateKey = freshPrivateKey(isCompressed)
def apply(): ECPrivateKey = ECPrivateKey.freshPrivateKey
/** Generates a fresh [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]] that has not been used before. */
def freshPrivateKey: ECPrivateKey = freshPrivateKey(true)
def freshPrivateKey(isCompressed: Boolean): ECPrivateKey = {
val priv = CryptoUtil.freshPrivateKey
ECPrivateKey.fromBytes(priv.bytes, isCompressed)
}
def freshPrivateKey: ECPrivateKey = CryptoUtil.freshPrivateKey
}
/** Created by chris on 2/16/16.
* A valid deserialized ECDSA public key.
*
* This class wraps some underlying _bytes but after checking that these _bytes are valid,
* all serializations (compressed and decompressed) of this public key are (lazily) computed
* where the decompressed version is used internally for computation and the compressed version
* is provided by the NetworkElement::bytes member.
*
* Note that 0x00 is not a valid ECPublicKey but is a valid SecpPoint meaning that if you are
* doing computations on public key (points) that may have intermediate 0x00 values, then you
* should convert using toPoint, do computation, and then convert back toPublicKey in the end.
*/
sealed abstract class ECPublicKey extends BaseECKey {
case class ECPublicKey(private val _bytes: ByteVector)
extends BaseECKey
with PublicKey[ECPublicKey] {
require(isFullyValid, s"Invalid public key: ${_bytes}")
def verify(hash: HashDigest, signature: ECDigitalSignature): Boolean =
verify(hash.bytes, signature)
/** Converts this public key into the raw underlying point on secp256k1 for computation. */
def toPoint: SecpPointFinite = SecpPoint.fromPublicKey(this)
/** Verifies if a given piece of data is signed by the
* [[org.bitcoins.crypto.ECPrivateKey ECPrivateKey]]'s corresponding
* [[org.bitcoins.crypto.ECPublicKey ECPublicKey]].
*/
def verify(data: ByteVector, signature: ECDigitalSignature): Boolean = {
CryptoUtil.verify(this, data, signature)
override private[crypto] def fromBytes(bytes: ByteVector): ECPublicKey = {
ECPublicKey.fromBytes(bytes)
}
def verify(hex: String, signature: ECDigitalSignature): Boolean =
verify(CryptoBytesUtil.decodeHex(hex), signature)
def schnorrVerify(
data: ByteVector,
signature: SchnorrDigitalSignature): Boolean = {
@ -200,9 +299,8 @@ sealed abstract class ECPublicKey extends BaseECKey {
def schnorrComputePoint(
data: ByteVector,
nonce: SchnorrNonce,
compressed: Boolean = isCompressed): ECPublicKey = {
schnorrPublicKey.computeSigPoint(data, nonce, compressed)
nonce: SchnorrNonce): ECPublicKey = {
schnorrPublicKey.computeSigPoint(data, nonce)
}
def schnorrPublicKey: SchnorrPublicKey = SchnorrPublicKey(bytes)
@ -224,31 +322,56 @@ sealed abstract class ECPublicKey extends BaseECKey {
override def toString: String = "ECPublicKey(" + hex + ")"
/** Checks if the [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] is compressed */
def isCompressed: Boolean = bytes.size == 33
/** Returns true only if the underlying wrapped _bytes are compressed */
override def isCompressed: Boolean = _bytes.size == 33
/** Checks if the [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] is valid according to secp256k1 */
def isFullyValid: Boolean = ECPublicKey.isFullyValid(bytes)
/** @inheritdoc */
override def isFullyValid: Boolean = ECPublicKey.isFullyValid(_bytes)
/** Returns the decompressed version of this [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] */
def decompressed: ECPublicKey =
CryptoUtil.decompressed(this)
def compressed: ECPublicKey = {
if (isCompressed || bytes == ByteVector.fromByte(0x00)) {
/** Returns this same ECPublicKey wrapping the underlying compressed _bytes.
* This function doesn't really have any use, don't use it probably.
*/
override lazy val compressed: ECPublicKey = {
if (isCompressed || _bytes == ByteVector.fromByte(0x00)) {
this
} else {
val key = if (bytes.length == 65) this else decompressed
val (x, y) = key.bytes.tail.splitAt(32)
val key = if (_bytes.length == 65) this else decompressed
val (x, y) = key._bytes.tail.splitAt(32)
val leadByte = if (FieldElement(y).isEven) 2.toByte else 3.toByte
ECPublicKey(x.+:(leadByte))
}
}
/** Returns the compressed representation of this ECPublicKey */
override def bytes: ByteVector = {
compressed._bytes
}
/** Returns the decompressed representation of this ECPublicKey */
def decompressedBytes: ByteVector = {
decompressed._bytes
}
def decompressedHex: String = {
decompressedBytes.toHex
}
/** Converts this ECPublicKey to raw ECPublicKeyBytes using the specified serialization. */
def toPublicKeyBytes(isCompressed: Boolean = true): ECPublicKeyBytes = {
val bs = if (isCompressed) bytes else decompressedBytes
ECPublicKeyBytes(bs)
}
override def equals(obj: Any): Boolean = {
obj match {
case pubKey: ECPublicKey => bytes == pubKey.bytes
case _ => false
}
}
/** 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.
* If you are adding more than two points together use CryptoUtil.combinePubKeys instead.
*/
def add(otherKey: ECPublicKey): ECPublicKey =
CryptoUtil.add(this, otherKey)
@ -260,28 +383,15 @@ sealed abstract class ECPublicKey extends BaseECKey {
object ECPublicKey extends Factory[ECPublicKey] {
private case class ECPublicKeyImpl(
override val bytes: ByteVector,
ec: ExecutionContext)
extends ECPublicKey {
//unfortunately we cannot place ANY invariants here
//because of old transactions on the blockchain that have weirdly formatted public keys. Look at example in script_tests.json
//https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_tests.json#L457
//bitcoin core only checks CPubKey::IsValid()
//this means we can have public keys with only one byte i.e. 0x00 or no bytes.
//Eventually we would like this to be CPubKey::IsFullyValid() but since we are remaining backwards compatible
//we cannot do this. If there ever is a hard fork this would be a good thing to add.
}
override def fromBytes(bytes: ByteVector): ECPublicKey = {
ECPublicKeyImpl(bytes, ExecutionContext.global)
new ECPublicKey(bytes)
}
def apply(): ECPublicKey = freshPublicKey
val dummy: ECPublicKey = FieldElement.one.getPublicKey
def apply(point: SecpPointFinite): ECPublicKey = point.toPublicKey
val infinity: ECPublicKey = ECPublicKey.fromBytes(ByteVector(0x00))
val dummy: ECPublicKey = FieldElement.one.getPublicKey
/** Generates a fresh [[org.bitcoins.crypto.ECPublicKey ECPublicKey]] that has not been used before. */
def freshPublicKey: ECPublicKey = ECPrivateKey.freshPrivateKey.publicKey

View file

@ -1,37 +0,0 @@
package org.bitcoins.crypto
import scodec.bits.ByteVector
import java.math.BigInteger
/** Represents a point on secp256k1 elliptic curve.
*/
sealed trait ECPoint
/** The infinity point.
*/
case object ECPointInfinity extends ECPoint
/** A point on an elliptic curve.
* @param x
* @param y
*/
case class ECPointImpl(x: FieldElement, y: FieldElement) extends ECPoint
object ECPoint {
def apply(x: ByteVector, y: ByteVector): ECPoint =
ECPointImpl(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
def apply(x: Array[Byte], y: Array[Byte]): ECPoint =
ECPointImpl(FieldElement.fromByteArray(x), FieldElement.fromByteArray(y))
def apply(x: BigInteger, y: BigInteger): ECPoint =
ECPointImpl(FieldElement(x), FieldElement(y))
def apply(x: BigInt, y: BigInt): ECPoint =
ECPointImpl(FieldElement(x), FieldElement(y))
def apply(x: String, y: String): ECPoint =
ECPointImpl(FieldElement.fromHex(x), FieldElement.fromHex(y))
}

View file

@ -0,0 +1,63 @@
package org.bitcoins.crypto
import scodec.bits.ByteVector
import java.math.BigInteger
/** Represents a point on the secp256k1 elliptic curve. */
sealed trait SecpPoint extends NetworkElement {
/** Returns the group sum of this point and the input. */
def add(point: SecpPoint): SecpPoint = {
CryptoUtil.add(this, point)
}
}
/** The point at infinity, this is the secp256k1 group identity element meaning
* p + 0x00 = 0x00 + p = p for any point p and
* p + (-p) = 0x00.
*
* Note that this does not correspond to a valid ECPublicKey just like
* FieldElement.zero does not correspond to a valid private key (and in fact
* 0x00 = FieldElement.zero*G).
*/
case object SecpPointInfinity extends SecpPoint {
override val bytes: ByteVector = ByteVector(0x00)
}
/** A non-identity point, (x, y), on the secp256k1 elliptic curve.
*/
case class SecpPointFinite(x: FieldElement, y: FieldElement) extends SecpPoint {
override def bytes: ByteVector = {
ByteVector(0x04) ++ x.bytes ++ y.bytes
}
def toPublicKey: ECPublicKey = {
ECPublicKey(bytes)
}
}
object SecpPoint {
def fromPublicKey(key: ECPublicKey): SecpPointFinite = {
val (x, y) = key.decompressedBytes.tail.splitAt(32)
SecpPointFinite(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
}
def apply(x: ByteVector, y: ByteVector): SecpPointFinite =
SecpPointFinite(FieldElement.fromBytes(x), FieldElement.fromBytes(y))
def apply(x: Array[Byte], y: Array[Byte]): SecpPointFinite =
SecpPointFinite(FieldElement.fromByteArray(x),
FieldElement.fromByteArray(y))
def apply(x: BigInteger, y: BigInteger): SecpPointFinite =
SecpPointFinite(FieldElement(x), FieldElement(y))
def apply(x: BigInt, y: BigInt): SecpPointFinite =
SecpPointFinite(FieldElement(x), FieldElement(y))
def apply(x: String, y: String): SecpPointFinite =
SecpPointFinite(FieldElement.fromHex(x), FieldElement.fromHex(y))
}

View file

@ -102,10 +102,10 @@ val psbtWithSigHashFlags = psbtWithUpdatedSecondInput
// correctly in an application
// Here we use the relevant private keys to sign the first input
val privKey0 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr")
"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr").toPrivateKey
val privKey1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d")
"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d").toPrivateKey
val psbtFirstSig =
psbtWithSigHashFlags

View file

@ -6,7 +6,7 @@ import org.bitcoins.core.number.{Int32, UInt32}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKey}
import org.bitcoins.crypto.{DoubleSha256Digest, ECPublicKeyBytes}
/** Created by chris on 2/12/16.
*/
@ -136,7 +136,7 @@ trait TransactionTestUtil {
Transaction,
Int,
ScriptPubKey,
Seq[ECPublicKey]) = {
Seq[ECPublicKeyBytes]) = {
val key1 = ECPrivateKeyUtil.fromWIFToPrivateKey(
"cVLwRLTvz3BxDAWkvS3yzT9pUcTCup7kQnfT2smRjvmmm1wAP6QT")
val key2 = ECPrivateKeyUtil.fromWIFToPrivateKey(
@ -146,7 +146,7 @@ trait TransactionTestUtil {
(signedMultiSignatureTx,
0,
multiSignatureScriptPubKey,
Seq(key1.publicKey, key2.publicKey, key3.publicKey))
Seq(key1.publicKeyBytes, key2.publicKeyBytes, key3.publicKeyBytes))
}
/** Returns a p2sh transaction with its corresponding crediting output */

View file

@ -214,7 +214,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield {
assert(signed != psbt)
assert(
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
signed.inputMaps.head.partialSignatures
.exists(_.pubKey.toPublicKey == walletKey))
}
}
@ -236,7 +237,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield {
assert(signed != psbt)
assert(
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
signed.inputMaps.head.partialSignatures
.exists(_.pubKey.toPublicKey == walletKey))
}
}
@ -258,7 +260,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield {
assert(signed != psbt)
assert(
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
signed.inputMaps.head.partialSignatures
.exists(_.pubKey.toPublicKey == walletKey))
}
}
@ -281,7 +284,8 @@ class WalletUnitTest extends BitcoinSWalletTest {
} yield {
assert(signed != psbt)
assert(
signed.inputMaps.head.partialSignatures.exists(_.pubKey == walletKey))
signed.inputMaps.head.partialSignatures
.exists(_.pubKey.toPublicKey == walletKey))
}
}

View file

@ -822,7 +822,10 @@ abstract class Wallet
keyPaths.foldLeft(withData) { (accum, hdPath) =>
val sign = keyManager.toSign(hdPath)
// Only sign if that key doesn't have a signature yet
if (!input.partialSignatures.exists(_.pubKey == sign.publicKey)) {
if (
!input.partialSignatures.exists(
_.pubKey.toPublicKey == sign.publicKey)
) {
logger.debug(
s"Signing input $index with key ${sign.publicKey.hex}")
accum.sign(index, sign)