Pull over scala3 compatible code for crypto/ project (#5575)

* Pull over scala3 compatible code for crypto/ project from https://github.com/bitcoin-s/bitcoin-s/pull/3497

* Add -Xsource:3 flag to crypto/

* Remove tests for constructors
This commit is contained in:
Chris Stewart 2024-05-09 17:07:05 -05:00 committed by GitHub
parent f707db0a0e
commit 3af204e74d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 460 additions and 860 deletions

View File

@ -36,6 +36,7 @@ lazy val commonJsSettings = {
lazy val crypto = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.settings(scalacOptions += "-Xsource:3")
.settings(
name := "bitcoin-s-crypto",
libraryDependencies ++= Deps.crypto.value

View File

@ -354,10 +354,6 @@ class AesCryptTest extends BitcoinSCryptoTest {
assertDoesNotCompile("""val k = AesKey(hex"1234")""")
}
it must "not have a constructor" in {
assertDoesNotCompile("""val k = new AesKey(hex"1234")""")
}
it must "not be constructable from bad byte lenghts" in {
val bytevectorGens: Seq[Gen[ByteVector]] =
(0 until 100)
@ -383,11 +379,6 @@ class AesCryptTest extends BitcoinSCryptoTest {
assertDoesNotCompile("""val iv = AesIV(hex"1234")""")
}
it must "not have a constructor" in {
assertDoesNotCompile("""val iv = new AesIV(hex"1234")""")
}
it must "not be constructable from invalid length bytes" in {
val bytes = hex"12345"
intercept[IllegalArgumentException] {
@ -401,10 +392,6 @@ class AesCryptTest extends BitcoinSCryptoTest {
assertDoesNotCompile("""val p = AesPassword("hi there")""")
}
it must "not have a constructor" in {
assertDoesNotCompile("""val p = new AesPassword("hi there")""")
}
it must "fail to create an empty AES password" in {
assert(AesPassword.fromStringOpt("").isEmpty)
intercept[IllegalArgumentException] {

View File

@ -67,10 +67,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def toPublicKey(privateKey: ECPrivateKeyBytes): ECPublicKey = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val pubKeyBuffer =
SECP256k1.publicKeyCreate(
key = buffer,
compressed = privateKey.isCompressed
)
SECP256k1.publicKeyCreate(key = buffer,
compressed = privateKey.isCompressed)
val privKeyByteVec = CryptoJsUtil.toByteVector(pubKeyBuffer)
ECPublicKey.fromBytes(privKeyByteVec)
}
@ -155,8 +153,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
*/
override def recoverPublicKey(
signature: ECDigitalSignature,
message: ByteVector
): (ECPublicKey, ECPublicKey) = {
message: ByteVector): (ECPublicKey, ECPublicKey) = {
val msgBuffer = CryptoJsUtil.toNodeBuffer(message)
val sigBuffer = CryptoJsUtil.toNodeBuffer(signature.bytes)
val keyBytes =
@ -190,8 +187,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def sign(
privateKey: ECPrivateKey,
dataToSign: ByteVector
): ECDigitalSignature = {
dataToSign: ByteVector): ECDigitalSignature = {
val privBuffer = CryptoJsUtil.toNodeBuffer(privateKey.bytes)
val dataBuffer = CryptoJsUtil.toNodeBuffer(dataToSign)
val buffer = SECP256k1.signDER(dataBuffer, privBuffer)
@ -202,8 +198,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def signWithEntropy(
privateKey: ECPrivateKey,
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature = ???
entropy: ByteVector): ECDigitalSignature = ???
override def secKeyVerify(privateKeybytes: ByteVector): Boolean = {
val buffer = CryptoJsUtil.toNodeBuffer(privateKeybytes)
@ -213,8 +208,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def verify(
publicKey: ECPublicKeyApi,
data: ByteVector,
signature: ECDigitalSignature
): Boolean = {
signature: ECDigitalSignature): Boolean = {
val dataBuffer = CryptoJsUtil.toNodeBuffer(data)
val sigBuffer = CryptoJsUtil.toNodeBuffer(signature.bytes)
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.bytes)
@ -223,8 +217,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement
): ECPublicKey = {
tweak: FieldElement): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(publicKey.decompressedBytes)
val tweakBuffer = CryptoJsUtil.toNodeBuffer(tweak.bytes)
val keyBuffer =
@ -238,10 +231,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
val pk2Buffer = CryptoJsUtil.toNodeBuffer(pk2.decompressedBytes)
try {
val keyBuffer =
SECP256k1.publicKeyCombine(
js.Array(pk1Buffer, pk2Buffer),
compress = true
)
SECP256k1.publicKeyCombine(js.Array(pk1Buffer, pk2Buffer),
compress = true)
val keyBytes = CryptoJsUtil.toByteVector(keyBuffer)
ECPublicKey.fromBytes(keyBytes)
} catch {
@ -252,8 +243,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
// check for infinity
if ((k1.head ^ k2.head) == 0x01 && k1.tail == k2.tail) {
throw new IllegalArgumentException(
s"Invalid public key sum, got 0x00 = $pk1 + $pk2"
)
s"Invalid public key sum, got 0x00 = $pk1 + $pk2")
} else {
throw ex
}
@ -262,8 +252,7 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey
): ECPublicKey = {
privkey: ECPrivateKey): ECPublicKey = {
val pubKeyBuffer = CryptoJsUtil.toNodeBuffer(pubkey.decompressedBytes)
val privKeyBuffer = CryptoJsUtil.toNodeBuffer(privkey.bytes)
val keyBuffer =
@ -297,10 +286,8 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
if (decoded.isInfinity())
SecpPointInfinity
else
SecpPoint(
new BigInteger(decoded.getX().toString()),
new BigInteger(decoded.getY().toString())
)
SecpPoint(new BigInteger(decoded.getX().toString()),
new BigInteger(decoded.getY().toString()))
}
}
@ -308,20 +295,17 @@ trait BCryptoCryptoRuntime extends CryptoRuntime {
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector = {
derivedKeyLength: Int): ByteVector = {
// bcrypto uses bytes instead of bits for length, so divide by 8
val keyLengthBytes = derivedKeyLength / 8
val buffer =
PBKDF2.derive(
sha512,
PBKDF2.derive(sha512,
CryptoJsUtil.toNodeBuffer(pass),
CryptoJsUtil.toNodeBuffer(salt),
iterationCount,
keyLengthBytes
)
keyLengthBytes)
CryptoJsUtil.toByteVector(buffer)
}
}

View File

@ -26,10 +26,8 @@ object CryptoJsUtil {
accum += entry.value
}
}
require(
accum.length == len,
s"Need $len bytes for buffer -> bytevector conversion"
)
require(accum.length == len,
s"Need $len bytes for buffer -> bytevector conversion")
ByteVector(accum.map(_.toByte))
}

View File

@ -67,30 +67,24 @@ class Buffer() extends js.Object {
* {{{new Buffer(arrayBuffer[, byteOffset[, length]])}}}
*/
@inline
@deprecated(
"Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0"
)
@deprecated("Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0")
def this(arrayBuffer: ArrayBuffer, byteOffset: Int, length: Int) = this()
/** @example
* {{{new Buffer(arrayBuffer[, byteOffset[, length]])}}}
*/
@inline
@deprecated(
"Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0"
)
@deprecated("Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0")
def this(arrayBuffer: ArrayBuffer, byteOffset: Int) = this()
/** @example
* {{{new Buffer(arrayBuffer[, byteOffset[, length]])}}}
*/
@inline
@deprecated(
"Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0"
)
@deprecated("Use Buffer.from(arrayBuffer[, byteOffset [, length]]) instead.",
since = "6.0.0")
def this(arrayBuffer: ArrayBuffer) = this()
/////////////////////////////////////////////////////////////////////////////////
@ -149,8 +143,7 @@ class Buffer() extends js.Object {
targetStart: Int = js.native,
targetEnd: Int = js.native,
sourceStart: Int = js.native,
sourceEnd: Int = js.native
): Int = js.native
sourceEnd: Int = js.native): Int = js.native
/** Copies data from a region of buf to a region in target even if the target
* memory region overlaps with buf.
@ -173,8 +166,7 @@ class Buffer() extends js.Object {
target: Buffer,
targetStart: Int = js.native,
sourceStart: Int = js.native,
sourceEnd: Int = js.native
): Int = js.native
sourceEnd: Int = js.native): Int = js.native
/** Creates and returns an iterator of [index, byte] pairs from the Buffer
* contents.
@ -215,8 +207,7 @@ class Buffer() extends js.Object {
value: Buffer | Int | String,
offset: Int = js.native,
end: Int = js.native,
encoding: String = js.native
): this.type = js.native
encoding: String = js.native): this.type = js.native
/** Returns the index of the first occurrence of value in buf or -1 if buf
* does not contain value
@ -235,8 +226,7 @@ class Buffer() extends js.Object {
def indexOf(
value: Buffer | Int | String,
byteOffset: Int = js.native,
encoding: String = js.native
): Int = js.native
encoding: String = js.native): Int = js.native
/** Equivalent to buf.indexOf() !== -1.
* @param value
@ -253,8 +243,7 @@ class Buffer() extends js.Object {
def includes(
value: Buffer | Int | String,
byteOffset: Int = js.native,
encoding: String = js.native
): Boolean =
encoding: String = js.native): Boolean =
js.native
/** Creates and returns an iterator of buf keys (indices).
@ -292,8 +281,7 @@ class Buffer() extends js.Object {
def lastIndexOf(
value: Buffer | Int | String,
byteOffset: Int = js.native,
encoding: String = js.native
): Int =
encoding: String = js.native): Int =
js.native
/** Returns the amount of memory allocated for buf in bytes. Note that this
@ -496,8 +484,7 @@ class Buffer() extends js.Object {
def readIntBE(
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Reads byteLength number of bytes from buf at the specified offset and
* interprets the result as a two's complement signed value. Supports up to
@ -520,8 +507,7 @@ class Buffer() extends js.Object {
def readIntLE(
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Reads an unsigned 8-bit integer from buf at the specified offset. Setting
* noAssert to true allows offset to be beyond the end of buf, but the result
@ -624,8 +610,7 @@ class Buffer() extends js.Object {
def readUIntBE(
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Reads byteLength number of bytes from buf at the specified offset and
* interprets the result as an unsigned integer. Supports up to 48 bits of
@ -648,8 +633,7 @@ class Buffer() extends js.Object {
def readUIntLE(
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Returns a new Buffer that references the same memory as the original, but
* offset and cropped by the start and end indices.
@ -722,8 +706,7 @@ class Buffer() extends js.Object {
def toString(
encoding: String = js.native,
start: Int = js.native,
end: Int = js.native
): String = js.native
end: Int = js.native): String = js.native
/** Re-encodes the given Buffer instance from one character encoding to
* another. Returns a new Buffer instance. Throws if the fromEnc or toEnc
@ -772,8 +755,7 @@ class Buffer() extends js.Object {
string: String,
offset: Int = js.native,
length: Int = js.native,
encoding: String = js.native
): Int =
encoding: String = js.native): Int =
js.native
/** Writes value to buf at the specified offset with specified endian format
@ -797,8 +779,7 @@ class Buffer() extends js.Object {
def writeDoubleBE(
value: Double,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeDoubleBE() writes big endian, writeDoubleLE() writes little endian).
@ -821,8 +802,7 @@ class Buffer() extends js.Object {
def writeDoubleLE(
value: Double,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeFloatBE() writes big endian, writeFloatLE() writes little endian).
@ -845,8 +825,7 @@ class Buffer() extends js.Object {
def writeFloatBE(
value: Float,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeFloatBE() writes big endian, writeFloatLE() writes little endian).
@ -869,8 +848,7 @@ class Buffer() extends js.Object {
def writeFloatLE(
value: Float,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset. value should be a valid
* signed 8-bit integer. Behavior is undefined when value is anything other
@ -917,8 +895,7 @@ class Buffer() extends js.Object {
def writeInt16BE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeInt16BE() writes big endian, writeInt16LE() writes little endian).
@ -943,8 +920,7 @@ class Buffer() extends js.Object {
def writeInt16LE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeInt32BE() writes big endian, writeInt32LE() writes little endian).
@ -969,8 +945,7 @@ class Buffer() extends js.Object {
def writeInt32BE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeInt32BE() writes big endian, writeInt32LE() writes little endian).
@ -995,8 +970,7 @@ class Buffer() extends js.Object {
def writeInt32LE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes byteLength bytes of value to buf at the specified offset. Supports
* up to 48 bits of accuracy. Behavior is undefined when value is anything
@ -1022,8 +996,7 @@ class Buffer() extends js.Object {
value: Int,
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes byteLength bytes of value to buf at the specified offset. Supports
* up to 48 bits of accuracy. Behavior is undefined when value is anything
@ -1049,8 +1022,7 @@ class Buffer() extends js.Object {
value: Int,
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset. value should be a valid
* unsigned 8-bit integer. Behavior is undefined when value is anything other
@ -1093,8 +1065,7 @@ class Buffer() extends js.Object {
def writeUInt16BE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeUInt16BE() writes big endian, writeUInt16LE() writes little endian).
@ -1117,8 +1088,7 @@ class Buffer() extends js.Object {
def writeUInt16LE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeUInt32BE() writes big endian, writeUInt32LE() writes little endian).
@ -1141,8 +1111,7 @@ class Buffer() extends js.Object {
def writeUInt32BE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes value to buf at the specified offset with specified endian format
* (writeUInt32BE() writes big endian, writeUInt32LE() writes little endian).
@ -1165,8 +1134,7 @@ class Buffer() extends js.Object {
def writeUInt32LE(
value: Int,
offset: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes byteLength bytes of value to buf at the specified offset. Supports
* up to 48 bits of accuracy. Behavior is undefined when value is anything
@ -1192,8 +1160,7 @@ class Buffer() extends js.Object {
value: Int,
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
/** Writes byteLength bytes of value to buf at the specified offset. Supports
* up to 48 bits of accuracy. Behavior is undefined when value is anything
@ -1219,8 +1186,7 @@ class Buffer() extends js.Object {
value: Int,
offset: Int,
byteLength: Int,
noAssert: Boolean = js.native
): Int = js.native
noAssert: Boolean = js.native): Int = js.native
}
@ -1273,8 +1239,7 @@ object Buffer extends js.Object {
def alloc(
size: Int,
fill: Buffer | Int | String = js.native,
encoding: String = js.native
): Buffer = js.native
encoding: String = js.native): Buffer = js.native
/** Calling Buffer.alloc(size) can be significantly slower than the
* alternative Buffer.allocUnsafe(size) but ensures that the newly created

View File

@ -17,6 +17,5 @@ object PBKDF2 extends js.Object {
pass: Buffer,
salt: Buffer,
iter: Int,
len: Int
): Buffer = js.native
len: Int): Buffer = js.native
}

View File

@ -44,15 +44,13 @@ object SECP256k1 extends js.Object {
msg: Buffer,
sig: Buffer,
param: Byte,
compress: Boolean
): Buffer = js.native
compress: Boolean): Buffer = js.native
def recoverDER(
msg: Buffer,
sig: Buffer,
param: Byte,
compress: Boolean
): Buffer = js.native
compress: Boolean): Buffer = js.native
val curve: js.Dynamic = js.native
}

View File

@ -9,10 +9,8 @@ object BouncyCastleCryptoParams {
/** The curve that bitcoin uses. */
val curve =
new ECDomainParameters(
params.getCurve,
new ECDomainParameters(params.getCurve,
params.getG,
params.getN,
params.getH
)
params.getH)
}

View File

@ -36,8 +36,7 @@ object BouncyCastleUtil {
private[crypto] def decodePubKey(
point: ECPoint,
isCompressed: Boolean = true
): ECPublicKey = {
isCompressed: Boolean = true): ECPublicKey = {
val bytes = point.getEncoded(isCompressed)
ECPublicKey.fromBytes(ByteVector(bytes))
}
@ -77,16 +76,12 @@ object BouncyCastleUtil {
def sign(
dataToSign: ByteVector,
privateKey: ECPrivateKey
): ECDigitalSignature = {
privateKey: ECPrivateKey): ECDigitalSignature = {
val signer: ECDSASigner = new ECDSASigner(
new HMacDSAKCalculator(new SHA256Digest())
)
new HMacDSAKCalculator(new SHA256Digest()))
val privKey: ECPrivateKeyParameters =
new ECPrivateKeyParameters(
getBigInteger(privateKey.bytes),
BouncyCastleCryptoParams.curve
)
new ECPrivateKeyParameters(getBigInteger(privateKey.bytes),
BouncyCastleCryptoParams.curve)
signer.init(true, privKey)
val components: Array[BigInteger] =
signer.generateSignature(dataToSign.toArray)
@ -99,8 +94,7 @@ object BouncyCastleUtil {
val signatureLowS = DERSignatureUtil.lowS(signature)
require(
signatureLowS.isDEREncoded,
"We must create DER encoded signatures when signing a piece of data, got: " + signatureLowS
)
"We must create DER encoded signatures when signing a piece of data, got: " + signatureLowS)
signatureLowS
}
@ -117,16 +111,12 @@ object BouncyCastleUtil {
def signWithEntropy(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
entropy: ByteVector
): ECDigitalSignature = {
entropy: ByteVector): ECDigitalSignature = {
val signer: ECDSASigner = new ECDSASigner(
new HMacDSAKCalculatorWithEntropy(new SHA256Digest(), entropy)
)
new HMacDSAKCalculatorWithEntropy(new SHA256Digest(), entropy))
val privKey: ECPrivateKeyParameters =
new ECPrivateKeyParameters(
getBigInteger(privateKey.bytes),
BouncyCastleCryptoParams.curve
)
new ECPrivateKeyParameters(getBigInteger(privateKey.bytes),
BouncyCastleCryptoParams.curve)
signer.init(true, privKey)
val components: Array[BigInteger] =
signer.generateSignature(dataToSign.toArray)
@ -139,32 +129,26 @@ object BouncyCastleUtil {
val signatureLowS = DERSignatureUtil.lowS(signature)
require(
signatureLowS.isDEREncoded,
"We must create DER encoded signatures when signing a piece of data, got: " + signatureLowS
)
"We must create DER encoded signatures when signing a piece of data, got: " + signatureLowS)
signatureLowS
}
def verifyDigitalSignature(
data: ByteVector,
publicKey: ECPublicKeyApi,
signature: ECDigitalSignature
): Boolean = {
signature: ECDigitalSignature): Boolean = {
val resultTry = Try {
val publicKeyParams =
new ECPublicKeyParameters(
decodePoint(publicKey.bytes),
BouncyCastleCryptoParams.curve
)
new ECPublicKeyParameters(decodePoint(publicKey.bytes),
BouncyCastleCryptoParams.curve)
val signer = new ECDSASigner
signer.init(false, publicKeyParams)
signature match {
case EmptyDigitalSignature =>
signer.verifySignature(
data.toArray,
signer.verifySignature(data.toArray,
java.math.BigInteger.valueOf(0),
java.math.BigInteger.valueOf(0)
)
java.math.BigInteger.valueOf(0))
case _: ECDigitalSignature =>
val (r, s) = signature.decodeSignature
signer.verifySignature(data.toArray, r.bigInteger, s.bigInteger)

View File

@ -18,7 +18,7 @@ import java.security.{MessageDigest, SecureRandom}
* Castle (https://bouncycastle.org/) and [[java.security]].
*/
trait BouncycastleCryptoRuntime extends CryptoRuntime {
private[this] lazy val secureRandom = new SecureRandom()
private lazy val secureRandom = new SecureRandom()
override val cryptoContext: CryptoContext = CryptoContext.BouncyCastle
@ -65,20 +65,16 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
bytes.tail
} else {
throw new IllegalArgumentException(
s"Field element cannot have more than 32 bytes, got $bytes from $x"
)
s"Field element cannot have more than 32 bytes, got $bytes from $x")
}
(
BouncyCastleUtil.decodePoint(ECPublicKey(0x02.toByte +: bytes32)),
BouncyCastleUtil.decodePoint(ECPublicKey(0x03.toByte +: bytes32))
)
(BouncyCastleUtil.decodePoint(ECPublicKey(0x02.toByte +: bytes32)),
BouncyCastleUtil.decodePoint(ECPublicKey(0x03.toByte +: bytes32)))
}
override def recoverPublicKey(
signature: ECDigitalSignature,
message: ByteVector
): (ECPublicKey, ECPublicKey) = {
message: ByteVector): (ECPublicKey, ECPublicKey) = {
val curve = BouncyCastleCryptoParams.curve
val (r, s) = (signature.r.bigInteger, signature.s.bigInteger)
@ -159,16 +155,14 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
override def sign(
privateKey: ECPrivateKey,
dataToSign: ByteVector
): ECDigitalSignature = {
dataToSign: ByteVector): ECDigitalSignature = {
BouncyCastleUtil.sign(dataToSign, privateKey)
}
override def signWithEntropy(
privateKey: ECPrivateKey,
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature =
entropy: ByteVector): ECDigitalSignature =
BouncyCastleUtil.signWithEntropy(bytes, privateKey, entropy)
override def secKeyVerify(privateKeyBytes: ByteVector): Boolean = {
@ -182,8 +176,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
override def verify(
publicKey: ECPublicKeyApi,
data: ByteVector,
signature: ECDigitalSignature
): Boolean =
signature: ECDigitalSignature): Boolean =
BouncyCastleUtil.verifyDigitalSignature(data, publicKey, signature)
override def publicKey(privateKey: ECPrivateKeyBytes): ECPublicKey =
@ -191,8 +184,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement
): ECPublicKey =
tweak: FieldElement): ECPublicKey =
BouncyCastleUtil.pubKeyTweakMul(publicKey, tweak.bytes)
override def add(pk1: ECPublicKey, pk2: ECPublicKey): ECPublicKey = {
@ -204,8 +196,7 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey
): ECPublicKey = {
privkey: ECPrivateKey): ECPublicKey = {
val tweak = privkey.publicKey
pubkey.add(tweak)
}
@ -233,18 +224,15 @@ trait BouncycastleCryptoRuntime extends CryptoRuntime {
if (decoded.isInfinity)
SecpPointInfinity
else
SecpPoint(
decoded.getRawXCoord.getEncoded,
decoded.getRawYCoord.getEncoded
)
SecpPoint(decoded.getRawXCoord.getEncoded,
decoded.getRawYCoord.getEncoded)
}
override def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector = {
derivedKeyLength: Int): ByteVector = {
val bytes =
PBKDF2.withSha512(pass, salt, iterationCount, derivedKeyLength).getEncoded
ByteVector(bytes)

View File

@ -42,8 +42,7 @@ class HMacDSAKCalculatorWithEntropy(digest: Digest, entropy: ByteVector)
override def init(
n: BigInteger,
d: BigInteger,
message: Array[Byte]
): Unit = {
message: Array[Byte]): Unit = {
this.n = n
Arrays.fill(V, 0x01.toByte)

View File

@ -16,8 +16,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def recoverPublicKey(
signature: ECDigitalSignature,
message: ByteVector
): (ECPublicKey, ECPublicKey) =
message: ByteVector): (ECPublicKey, ECPublicKey) =
BouncycastleCryptoRuntime.recoverPublicKey(signature, message)
override def hmac512(key: ByteVector, data: ByteVector): ByteVector =
@ -52,18 +51,15 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def toPublicKey(privateKey: ECPrivateKeyBytes): ECPublicKey = {
val pubKeyBytes: Array[Byte] =
NativeSecp256k1.computePubkey(
privateKey.bytes.toArray,
privateKey.isCompressed
)
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
privateKey.isCompressed)
val pubBytes = ByteVector(pubKeyBytes)
ECPublicKey(pubBytes)
}
override def sign(
privateKey: ECPrivateKey,
dataToSign: ByteVector
): ECDigitalSignature = {
dataToSign: ByteVector): ECDigitalSignature = {
val signature =
NativeSecp256k1.sign(dataToSign.toArray, privateKey.bytes.toArray)
ECDigitalSignature(ByteVector(signature))
@ -72,13 +68,10 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def signWithEntropy(
privateKey: ECPrivateKey,
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature = {
val sigBytes = NativeSecp256k1.signWithEntropy(
bytes.toArray,
entropy: ByteVector): ECDigitalSignature = {
val sigBytes = NativeSecp256k1.signWithEntropy(bytes.toArray,
privateKey.bytes.toArray,
entropy.toArray
)
entropy.toArray)
ECDigitalSignature(ByteVector(sigBytes))
}
@ -89,14 +82,11 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def verify(
publicKey: ECPublicKeyApi,
data: ByteVector,
signature: ECDigitalSignature
): Boolean = {
signature: ECDigitalSignature): Boolean = {
val result =
NativeSecp256k1.verify(
data.toArray,
NativeSecp256k1.verify(data.toArray,
signature.bytes.toArray,
publicKey.bytes.toArray
)
publicKey.bytes.toArray)
if (!result) {
// if signature verification fails with libsecp256k1 we need to use our old
@ -116,23 +106,18 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def publicKey(privateKey: ECPrivateKeyBytes): ECPublicKey = {
val pubKeyBytes: Array[Byte] =
NativeSecp256k1.computePubkey(
privateKey.bytes.toArray,
privateKey.isCompressed
)
NativeSecp256k1.computePubkey(privateKey.bytes.toArray,
privateKey.isCompressed)
val pubBytes = ByteVector(pubKeyBytes)
ECPublicKey(pubBytes)
}
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement
): ECPublicKey = {
val mulBytes = NativeSecp256k1.pubKeyTweakMul(
publicKey.bytes.toArray,
tweak: FieldElement): ECPublicKey = {
val mulBytes = NativeSecp256k1.pubKeyTweakMul(publicKey.bytes.toArray,
tweak.bytes.toArray,
true
)
true)
ECPublicKey(ByteVector(mulBytes))
}
@ -158,8 +143,7 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def combinePubKeys(
pubKeys: Vector[ECPublicKey],
isCompressed: Boolean = true
): ECPublicKey = {
isCompressed: Boolean = true): ECPublicKey = {
val summands = pubKeys.map(_.decompressedBytes.toArray).toArray
val sumKey = NativeSecp256k1.pubKeyCombine(summands, isCompressed)
@ -168,54 +152,43 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey
): ECPublicKey = {
privkey: ECPrivateKey): ECPublicKey = {
val tweaked = NativeSecp256k1.pubKeyTweakAdd(
pubkey.decompressedBytes.toArray,
privkey.bytes.toArray,
true
)
true)
ECPublicKey(ByteVector(tweaked))
}
override def schnorrSign(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
auxRand: ByteVector
): SchnorrDigitalSignature = {
auxRand: ByteVector): SchnorrDigitalSignature = {
val sigBytes =
NativeSecp256k1.schnorrSign(
dataToSign.toArray,
NativeSecp256k1.schnorrSign(dataToSign.toArray,
privateKey.bytes.toArray,
auxRand.toArray
)
auxRand.toArray)
SchnorrDigitalSignature(ByteVector(sigBytes))
}
override def schnorrSignWithNonce(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
nonceKey: ECPrivateKey
): SchnorrDigitalSignature = {
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
val sigBytes =
NativeSecp256k1.schnorrSignWithNonce(
dataToSign.toArray,
NativeSecp256k1.schnorrSignWithNonce(dataToSign.toArray,
privateKey.bytes.toArray,
nonceKey.bytes.toArray
)
nonceKey.bytes.toArray)
SchnorrDigitalSignature(ByteVector(sigBytes))
}
override def schnorrVerify(
data: ByteVector,
schnorrPubKey: SchnorrPublicKey,
signature: SchnorrDigitalSignature
): Boolean = {
NativeSecp256k1.schnorrVerify(
signature.bytes.toArray,
signature: SchnorrDigitalSignature): Boolean = {
NativeSecp256k1.schnorrVerify(signature.bytes.toArray,
data.toArray,
schnorrPubKey.bytes.toArray
)
schnorrPubKey.bytes.toArray)
}
// TODO: add a native implementation
@ -223,53 +196,43 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
data: ByteVector,
nonce: SchnorrNonce,
pubKey: SchnorrPublicKey,
compressed: Boolean
): ECPublicKey = {
BouncycastleCryptoRuntime.schnorrComputeSigPoint(
data,
compressed: Boolean): ECPublicKey = {
BouncycastleCryptoRuntime.schnorrComputeSigPoint(data,
nonce,
pubKey,
compressed
)
compressed)
}
override def adaptorSign(
key: ECPrivateKey,
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature = {
auxRand: ByteVector): ECAdaptorSignature = {
val sig = NativeSecp256k1.adaptorSign(
key.bytes.toArray,
adaptorPoint.decompressedBytes.toArray,
msg.toArray,
auxRand.toArray
)
auxRand.toArray)
ECAdaptorSignature(ByteVector(sig))
}
override def adaptorComplete(
key: ECPrivateKey,
adaptorSignature: ECAdaptorSignature
): ECDigitalSignature = {
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
val sigBytes =
NativeSecp256k1.adaptorAdapt(
key.bytes.toArray,
adaptorSignature.bytes.toArray
)
NativeSecp256k1.adaptorAdapt(key.bytes.toArray,
adaptorSignature.bytes.toArray)
ECDigitalSignature.fromBytes(ByteVector(sigBytes))
}
override def extractAdaptorSecret(
signature: ECDigitalSignature,
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey
): ECPrivateKey = {
key: ECPublicKey): ECPrivateKey = {
val secretBytes = NativeSecp256k1.adaptorExtractSecret(
signature.bytes.toArray,
adaptorSignature.bytes.toArray,
key.decompressedBytes.toArray
)
key.decompressedBytes.toArray)
ECPrivateKey(ByteVector(secretBytes))
}
@ -278,19 +241,15 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey,
msg: ByteVector,
adaptorPoint: ECPublicKey
): Boolean = {
NativeSecp256k1.adaptorVerify(
adaptorSignature.bytes.toArray,
adaptorPoint: ECPublicKey): Boolean = {
NativeSecp256k1.adaptorVerify(adaptorSignature.bytes.toArray,
key.decompressedBytes.toArray,
msg.toArray,
adaptorPoint.decompressedBytes.toArray
)
adaptorPoint.decompressedBytes.toArray)
}
override def isValidSignatureEncoding(
signature: ECDigitalSignature
): Boolean =
signature: ECDigitalSignature): Boolean =
BouncycastleCryptoRuntime.isValidSignatureEncoding(signature)
override def isDEREncoded(signature: ECDigitalSignature): Boolean =
@ -320,14 +279,11 @@ trait LibSecp256k1CryptoRuntime extends CryptoRuntime {
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector = {
BouncycastleCryptoRuntime.pbkdf2WithSha512(
pass,
derivedKeyLength: Int): ByteVector = {
BouncycastleCryptoRuntime.pbkdf2WithSha512(pass,
salt,
iterationCount,
derivedKeyLength
)
derivedKeyLength)
}
}

View File

@ -29,8 +29,7 @@ object PBKDF2 {
bytes: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): SecretKey = {
derivedKeyLength: Int): SecretKey = {
val keySpec = new PBEKeySpec(
bytes.toArray.map(_.toChar),

View File

@ -26,8 +26,7 @@ object AdaptorUtil {
privKey: ECPrivateKey,
adaptorPoint: ECPublicKey,
algoName: String,
auxRand: ByteVector
): FieldElement = {
auxRand: ByteVector): FieldElement = {
val randHash = CryptoUtil.sha256ECDSAAdaptorAux(auxRand).bytes
val maskedKey = randHash.xor(privKey.bytes)
@ -49,13 +48,11 @@ object AdaptorUtil {
dataToSign: ByteVector,
k: FieldElement,
r: ECPublicKey,
privateKey: ECPrivateKey
): FieldElement = {
privateKey: ECPrivateKey): FieldElement = {
CryptoUtil.decodePoint(r) match {
case SecpPointInfinity =>
throw new IllegalArgumentException(
s"Invalid point, got=$SecpPointInfinity"
)
s"Invalid point, got=$SecpPointInfinity")
case point: SecpPointFinite =>
val rx = FieldElement(point.x.toBigInteger)
val x = privateKey.fieldElement
@ -73,15 +70,12 @@ object AdaptorUtil {
privateKey: ECPrivateKey,
adaptorPoint: ECPublicKey,
dataToSign: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature = {
val k = adaptorNonce(
dataToSign,
auxRand: ByteVector): ECAdaptorSignature = {
val k = adaptorNonce(dataToSign,
privateKey,
adaptorPoint,
"ECDSAadaptor/non",
auxRand
)
auxRand)
if (k.isZero) {
throw new RuntimeException("Nonce cannot be zero.")
@ -106,8 +100,7 @@ object AdaptorUtil {
rx: FieldElement,
s: FieldElement,
pubKey: ECPublicKey,
msg: ByteVector
): FieldElement = {
msg: ByteVector): FieldElement = {
val m = FieldElement(msg)
val untweakedPoint =
m.getPublicKey.add(pubKey.multiply(rx)).multiply(s.inverse)
@ -121,8 +114,7 @@ object AdaptorUtil {
adaptorSig: ECAdaptorSignature,
pubKey: ECPublicKey,
data: ByteVector,
adaptor: ECPublicKey
): Boolean = {
adaptor: ECPublicKey): Boolean = {
val validProof = DLEQUtil.dleqVerify(
adaptorSig.dleqProofS,
adaptorSig.dleqProofE,
@ -133,8 +125,7 @@ object AdaptorUtil {
if (validProof) {
val tweakedNoncex = FieldElement(
CurveCoordinate(adaptorSig.tweakedNonce.bytes.tail).toBigInteger
)
CurveCoordinate(adaptorSig.tweakedNonce.bytes.tail).toBigInteger)
val untweakedNoncex = FieldElement(adaptorSig.untweakedNonce.bytes.tail)
if (tweakedNoncex.isZero || untweakedNoncex.isZero) {
@ -156,15 +147,12 @@ object AdaptorUtil {
*/
def adaptorComplete(
adaptorSecret: ECPrivateKey,
adaptorSig: ECAdaptorSignature
): ECDigitalSignature = {
adaptorSig: ECAdaptorSignature): ECDigitalSignature = {
val rx = FieldElement(adaptorSig.tweakedNonce.bytes.tail)
val correctedS = adaptorSig.adaptedS.multInv(adaptorSecret.fieldElement)
val sig = ECDigitalSignature.fromRS(
BigInt(rx.toBigInteger),
BigInt(correctedS.toBigInteger)
)
val sig = ECDigitalSignature.fromRS(BigInt(rx.toBigInteger),
BigInt(correctedS.toBigInteger))
DERSignatureUtil.lowS(sig)
}
@ -174,19 +162,14 @@ object AdaptorUtil {
def extractAdaptorSecret(
sig: ECDigitalSignature,
adaptorSig: ECAdaptorSignature,
adaptor: ECPublicKey
): ECPrivateKey = {
require(
adaptorSig.tweakedNonce.bytes.tail == sig.rBytes,
"Adaptor signature must be related to signature"
)
adaptor: ECPublicKey): ECPrivateKey = {
require(adaptorSig.tweakedNonce.bytes.tail == sig.rBytes,
"Adaptor signature must be related to signature")
val secretOrNeg = adaptorSig.adaptedS.multInv(FieldElement(sig.s))
require(
secretOrNeg.getPublicKey.bytes.tail == adaptor.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

@ -10,7 +10,7 @@ import scala.util.{Failure, Success, Try}
* vector (IV). Both the cipher text and the IV is needed to decrypt the cipher
* text.
*/
final case class AesEncryptedData(cipherText: ByteVector, iv: AesIV)
case class AesEncryptedData(cipherText: ByteVector, iv: AesIV)
extends NetworkElement {
/** We serialize IV and ciphertext by prepending the IV to the ciphertext, and
@ -59,8 +59,7 @@ object AesEncryptedData extends Factory[AesEncryptedData] {
override def fromBytes(bytes: ByteVector): AesEncryptedData = {
require(
bytes.length > AesIV.length,
s"AesEncryptedData must be longer than ${AesIV.length} bytes, got $bytes"
)
s"AesEncryptedData must be longer than ${AesIV.length} bytes, got $bytes")
val (ivBytes, cipherText) = bytes.splitAt(AesIV.length)
val iv = AesIV.fromValidBytes(ivBytes)
AesEncryptedData(cipherText, iv)
@ -69,9 +68,7 @@ object AesEncryptedData extends Factory[AesEncryptedData] {
/** Represents a salt used to derive a AES key from a human-readable passphrase.
*/
final case class AesSalt(
bytes: ByteVector
) extends AnyVal
case class AesSalt(bytes: ByteVector) extends AnyVal
object AesSalt extends Factory[AesSalt] {
@ -87,8 +84,7 @@ object AesSalt extends Factory[AesSalt] {
// we enforce the non-empty password length in the companion object
// to be able to make this extend AnyVal, and not be boxed at runtime
final case class AesPassword private (private val value: String)
extends MaskedToString {
case class AesPassword(private val value: String) extends MaskedToString {
/** Converts this password into an AES key
*
@ -116,8 +112,7 @@ final case class AesPassword private (private val value: String)
passwordBytes,
salt.bytes,
iterationCount = AesPassword.ITERATIONS,
derivedKeyLength = AesPassword.KEY_SIZE
)
derivedKeyLength = AesPassword.KEY_SIZE)
AesKey.fromValidBytes(secretKey)
}
@ -145,8 +140,7 @@ object AesPassword extends StringFactory[AesPassword] {
case Some(password) => password
case None =>
sys.error(
s"Could not construct AesPassword from given string, not logging in case it's sensitive"
)
s"Could not construct AesPassword from given string, not logging in case it's sensitive")
}
}
@ -156,15 +150,13 @@ object AesPassword extends StringFactory[AesPassword] {
def fromNonEmptyString(string: String): AesPassword =
fromStringOpt(string).getOrElse(
throw new IllegalArgumentException(
"Cannot construct empty AES passwords!"
)
)
"Cannot construct empty AES passwords!"))
}
/** Represents a encryption/decryption key. AES keys can be converted to
* [[javax.crypto.SecretKey SecretKey]]s, and have certain length requirements.
*/
final case class AesKey private (bytes: ByteVector)
case class AesKey(bytes: ByteVector)
extends MaskedToString
with NetworkElement {
@ -206,9 +198,7 @@ object AesKey {
def fromValidBytes(bytes: ByteVector): AesKey = {
fromBytes(bytes).getOrElse(
throw new IllegalArgumentException(
s"Given bytes (${bytes.toHex}) had bad length"
)
)
s"Given bytes (${bytes.toHex}) had bad length"))
}
/** Allowed AES key lengths, bytes */
@ -231,9 +221,7 @@ object AesKey {
/** Represents an initialization vector (IV) used in AES encryption.
*/
final case class AesIV private (bytes: ByteVector)
extends AnyVal
with NetworkElement
case class AesIV(bytes: ByteVector) extends AnyVal with NetworkElement
object AesIV {
@ -256,9 +244,7 @@ object AesIV {
def fromValidBytes(bytes: ByteVector): AesIV =
fromBytes(bytes).getOrElse(
throw new IllegalArgumentException(
s"Given bytes must be of length 16! Got: ${bytes.length}"
)
)
s"Given bytes must be of length 16! Got: ${bytes.length}"))
/** Generates a random IV */
def random: AesIV = {
@ -282,14 +268,11 @@ object AesCrypt {
private def decryptionCipher(
secret: AesKey,
initializationVector: AesIV
): Cipher = {
initializationVector: AesIV): Cipher = {
val cipher = getCipher
cipher.init(
Cipher.DECRYPT_MODE,
cipher.init(Cipher.DECRYPT_MODE,
secret.toSecretKey,
new IvParameterSpec(initializationVector.bytes.toArray)
)
new IvParameterSpec(initializationVector.bytes.toArray))
cipher
}
@ -297,8 +280,7 @@ object AesCrypt {
*/
def decrypt(
encrypted: AesEncryptedData,
key: AesKey
): Either[AesDecryptionException, ByteVector] = {
key: AesKey): Either[AesDecryptionException, ByteVector] = {
val cipher = decryptionCipher(key, encrypted.iv)
val decryptionAttempt = Try {
@ -319,11 +301,9 @@ object AesCrypt {
private def encryptionCipher(secret: AesKey, iv: AesIV): Cipher = {
val cipher = getCipher
cipher.init(
Cipher.ENCRYPT_MODE,
cipher.init(Cipher.ENCRYPT_MODE,
secret.toSecretKey,
new IvParameterSpec(iv.bytes.toArray)
)
new IvParameterSpec(iv.bytes.toArray))
cipher
}
@ -335,8 +315,7 @@ object AesCrypt {
private[crypto] def encryptWithIV(
plainText: ByteVector,
iv: AesIV,
key: AesKey
): AesEncryptedData = {
key: AesKey): AesEncryptedData = {
val cipher = encryptionCipher(key, iv)
val cipherText = cipher.doFinal(plainText.toArray)

View File

@ -9,7 +9,7 @@ import scala.util.{Failure, Success, Try}
*/
trait CryptoRuntime {
val cryptoContext: CryptoContext
def cryptoContext: CryptoContext
/** Generates a 32 byte private key */
def freshPrivateKey: ECPrivateKey
@ -166,8 +166,7 @@ trait CryptoRuntime {
*/
def recoverPublicKey(
signature: ECDigitalSignature,
message: ByteVector
): (ECPublicKey, ECPublicKey)
message: ByteVector): (ECPublicKey, ECPublicKey)
// The tag "BIP0340/aux"
private val schnorrAuxTagBytes = {
@ -200,16 +199,14 @@ trait CryptoRuntime {
def signWithEntropy(
privateKey: ECPrivateKey,
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature
entropy: ByteVector): ECDigitalSignature
def secKeyVerify(privateKeybytes: ByteVector): Boolean
def verify(
publicKey: ECPublicKeyApi,
data: ByteVector,
signature: ECDigitalSignature
): Boolean
signature: ECDigitalSignature): Boolean
def decompressed(pubKeyBytes: ByteVector): ByteVector = {
decodePoint(pubKeyBytes) match {
@ -223,8 +220,7 @@ trait CryptoRuntime {
def decompressed[PK <: ECPublicKeyApi](
pubKeyBytes: ByteVector,
fromBytes: ByteVector => PK
): PK = {
fromBytes: ByteVector => PK): PK = {
fromBytes(decompressed(pubKeyBytes))
}
@ -294,15 +290,13 @@ trait CryptoRuntime {
*/
def combinePubKeys(
pubKeys: Vector[ECPublicKey],
isCompressed: Boolean = true
): ECPublicKey = {
isCompressed: Boolean = true): 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."
)
"Sum result was 0x00, an invalid public key.")
case p: SecpPointFinite =>
if (isCompressed) p.toPublicKey.compressed
else p.toPublicKey.decompressed
@ -325,8 +319,7 @@ trait CryptoRuntime {
def schnorrSign(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
auxRand: ByteVector
): SchnorrDigitalSignature = {
auxRand: ByteVector): SchnorrDigitalSignature = {
val nonceKey =
SchnorrNonce.kFromBipSchnorr(privateKey, dataToSign, auxRand)
@ -336,14 +329,12 @@ trait CryptoRuntime {
def schnorrSignWithNonce(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
nonceKey: ECPrivateKey
): SchnorrDigitalSignature = {
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
val rx = nonceKey.schnorrNonce
val k = nonceKey.nonceKey.fieldElement
val x = privateKey.schnorrKey.fieldElement
val e = sha256SchnorrChallenge(
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign
).bytes
rx.bytes ++ privateKey.schnorrPublicKey.bytes ++ dataToSign).bytes
val challenge = x.multiply(FieldElement(e))
val sig = k.add(challenge)
@ -354,16 +345,14 @@ trait CryptoRuntime {
def schnorrVerify(
data: ByteVector,
schnorrPubKey: SchnorrPublicKey,
signature: SchnorrDigitalSignature
): Boolean = {
signature: SchnorrDigitalSignature): Boolean = {
val rx = signature.rx
val sT = Try(signature.sig.toPrivateKey)
sT match {
case Success(s) =>
val eBytes = sha256SchnorrChallenge(
rx.bytes ++ schnorrPubKey.bytes ++ data
).bytes
rx.bytes ++ schnorrPubKey.bytes ++ data).bytes
val e = FieldElement(eBytes)
val negE = e.negate
@ -384,11 +373,9 @@ trait CryptoRuntime {
data: ByteVector,
nonce: SchnorrNonce,
pubKey: SchnorrPublicKey,
compressed: Boolean
): ECPublicKey = {
compressed: Boolean): ECPublicKey = {
val eBytes = sha256SchnorrChallenge(
nonce.bytes ++ pubKey.bytes ++ data
).bytes
nonce.bytes ++ pubKey.bytes ++ data).bytes
val e = FieldElement(eBytes)
@ -406,23 +393,20 @@ trait CryptoRuntime {
key: ECPrivateKey,
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature = {
auxRand: ByteVector): ECAdaptorSignature = {
AdaptorUtil.adaptorSign(key, adaptorPoint, msg, auxRand)
}
def adaptorComplete(
key: ECPrivateKey,
adaptorSignature: ECAdaptorSignature
): ECDigitalSignature = {
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
AdaptorUtil.adaptorComplete(key, adaptorSignature)
}
def extractAdaptorSecret(
signature: ECDigitalSignature,
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey
): ECPrivateKey = {
key: ECPublicKey): ECPrivateKey = {
AdaptorUtil.extractAdaptorSecret(signature, adaptorSignature, key)
}
@ -430,8 +414,7 @@ trait CryptoRuntime {
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey,
msg: ByteVector,
adaptorPoint: ECPublicKey
): Boolean =
adaptorPoint: ECPublicKey): Boolean =
AdaptorUtil.adaptorVerify(adaptorSignature, key, msg, adaptorPoint)
def decodeSignature(signature: ECDigitalSignature): (BigInt, BigInt) =
@ -451,22 +434,18 @@ trait CryptoRuntime {
pass: String,
salt: String,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector = {
pbkdf2WithSha512(
ByteVector(pass.getBytes),
derivedKeyLength: Int): ByteVector = {
pbkdf2WithSha512(ByteVector(pass.getBytes),
ByteVector(salt.getBytes),
iterationCount,
derivedKeyLength
)
derivedKeyLength)
}
def pbkdf2WithSha512(
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector
derivedKeyLength: Int): ByteVector
def randomBytes(n: Int): ByteVector

View File

@ -84,8 +84,7 @@ trait CryptoUtil extends CryptoRuntime {
override def recoverPublicKey(
signature: ECDigitalSignature,
message: ByteVector
): (ECPublicKey, ECPublicKey) = {
message: ByteVector): (ECPublicKey, ECPublicKey) = {
cryptoRuntime.recoverPublicKey(signature, message)
}
@ -94,26 +93,22 @@ trait CryptoUtil extends CryptoRuntime {
override def sign(
privateKey: ECPrivateKey,
dataToSign: ByteVector
): ECDigitalSignature = {
dataToSign: ByteVector): ECDigitalSignature = {
val sig = cryptoRuntime.sign(privateKey, dataToSign)
assert(
verify(privateKey.publicKey, dataToSign, sig),
"Something has gone wrong, a generated signature may have been corrupted"
)
"Something has gone wrong, a generated signature may have been corrupted")
sig
}
override def signWithEntropy(
privateKey: ECPrivateKey,
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature = {
entropy: ByteVector): ECDigitalSignature = {
val sig = cryptoRuntime.signWithEntropy(privateKey, bytes, entropy)
assert(
verify(privateKey.publicKey, bytes, sig),
"Something has gone wrong, a generated signature may have been corrupted"
)
"Something has gone wrong, a generated signature may have been corrupted")
sig
}
@ -123,22 +118,19 @@ trait CryptoUtil extends CryptoRuntime {
override def verify(
publicKey: ECPublicKeyApi,
data: ByteVector,
signature: ECDigitalSignature
): Boolean =
signature: ECDigitalSignature): Boolean =
cryptoRuntime.verify(publicKey, data, signature)
override def decompressed(pubKeyBytes: ByteVector): ByteVector =
cryptoRuntime.decompressed(pubKeyBytes)
override def decompressed[PK <: ECPublicKeyApi](
publicKey: PK
): publicKey.type =
publicKey: PK): publicKey.type =
cryptoRuntime.decompressed(publicKey)
override def tweakMultiply(
publicKey: ECPublicKey,
tweak: FieldElement
): ECPublicKey =
tweak: FieldElement): ECPublicKey =
cryptoRuntime.tweakMultiply(publicKey, tweak)
override def add(pk1: ECPrivateKey, pk2: ECPrivateKey): ECPrivateKey =
@ -155,14 +147,12 @@ trait CryptoUtil extends CryptoRuntime {
override def combinePubKeys(
pubKeys: Vector[ECPublicKey],
isCompressed: Boolean = true
): ECPublicKey =
isCompressed: Boolean = true): ECPublicKey =
cryptoRuntime.combinePubKeys(pubKeys, isCompressed)
override def pubKeyTweakAdd(
pubkey: ECPublicKey,
privkey: ECPrivateKey
): ECPublicKey =
privkey: ECPrivateKey): ECPublicKey =
cryptoRuntime.pubKeyTweakAdd(pubkey, privkey)
override def isValidPubKey(pubKey: ECPublicKeyApi): Boolean =
@ -171,82 +161,70 @@ trait CryptoUtil extends CryptoRuntime {
override def schnorrSign(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
auxRand: ByteVector
): SchnorrDigitalSignature = {
auxRand: ByteVector): SchnorrDigitalSignature = {
val sig = cryptoRuntime.schnorrSign(dataToSign, privateKey, auxRand)
assert(
schnorrVerify(dataToSign, privateKey.schnorrPublicKey, sig),
"Something has gone wrong, a generated signature may have been corrupted"
)
"Something has gone wrong, a generated signature may have been corrupted")
sig
}
override def schnorrSignWithNonce(
dataToSign: ByteVector,
privateKey: ECPrivateKey,
nonceKey: ECPrivateKey
): SchnorrDigitalSignature = {
nonceKey: ECPrivateKey): SchnorrDigitalSignature = {
val sig =
cryptoRuntime.schnorrSignWithNonce(dataToSign, privateKey, nonceKey)
assert(
schnorrVerify(dataToSign, privateKey.schnorrPublicKey, sig),
"Something has gone wrong, a generated signature may have been corrupted"
)
"Something has gone wrong, a generated signature may have been corrupted")
sig
}
override def schnorrVerify(
data: ByteVector,
schnorrPubKey: SchnorrPublicKey,
signature: SchnorrDigitalSignature
): Boolean =
signature: SchnorrDigitalSignature): Boolean =
cryptoRuntime.schnorrVerify(data, schnorrPubKey, signature)
override def schnorrComputeSigPoint(
data: ByteVector,
nonce: SchnorrNonce,
pubKey: SchnorrPublicKey,
compressed: Boolean
): ECPublicKey =
compressed: Boolean): ECPublicKey =
cryptoRuntime.schnorrComputeSigPoint(data, nonce, pubKey, compressed)
override def adaptorSign(
key: ECPrivateKey,
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature =
auxRand: ByteVector): ECAdaptorSignature =
cryptoRuntime.adaptorSign(key, adaptorPoint, msg, auxRand)
override def adaptorComplete(
key: ECPrivateKey,
adaptorSignature: ECAdaptorSignature
): ECDigitalSignature =
adaptorSignature: ECAdaptorSignature): ECDigitalSignature =
cryptoRuntime.adaptorComplete(key, adaptorSignature)
override def extractAdaptorSecret(
signature: ECDigitalSignature,
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey
): ECPrivateKey =
key: ECPublicKey): ECPrivateKey =
cryptoRuntime.extractAdaptorSecret(signature, adaptorSignature, key)
override def adaptorVerify(
adaptorSignature: ECAdaptorSignature,
key: ECPublicKey,
msg: ByteVector,
adaptorPoint: ECPublicKey
): Boolean =
adaptorPoint: ECPublicKey): Boolean =
cryptoRuntime.adaptorVerify(adaptorSignature, key, msg, adaptorPoint)
override def decodeSignature(
signature: ECDigitalSignature
): (BigInt, BigInt) =
signature: ECDigitalSignature): (BigInt, BigInt) =
cryptoRuntime.decodeSignature(signature)
override def isValidSignatureEncoding(
signature: ECDigitalSignature
): Boolean =
signature: ECDigitalSignature): Boolean =
cryptoRuntime.isValidSignatureEncoding(signature)
override def isDEREncoded(signature: ECDigitalSignature): Boolean =
@ -264,8 +242,7 @@ trait CryptoUtil extends CryptoRuntime {
pass: ByteVector,
salt: ByteVector,
iterationCount: Int,
derivedKeyLength: Int
): ByteVector =
derivedKeyLength: Int): ByteVector =
cryptoRuntime.pbkdf2WithSha512(pass, salt, iterationCount, derivedKeyLength)
}

View File

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

View File

@ -19,8 +19,7 @@ object DLEQUtil {
def dleqPair(
fe: FieldElement,
adaptorPoint: ECPublicKey
): (ECPublicKey, ECPublicKey) = {
adaptorPoint: ECPublicKey): (ECPublicKey, ECPublicKey) = {
val point = fe.getPublicKey
val tweakedPoint = adaptorPoint.multiply(fe)
@ -35,19 +34,16 @@ object DLEQUtil {
adaptorPoint: ECPublicKey,
point: ECPublicKey,
tweakedPoint: ECPublicKey,
auxRand: ByteVector
): FieldElement = {
auxRand: ByteVector): FieldElement = {
val hash = CryptoUtil
.sha256(point.bytes ++ tweakedPoint.bytes)
.bytes
AdaptorUtil.adaptorNonce(
hash,
AdaptorUtil.adaptorNonce(hash,
fe.toPrivateKey,
adaptorPoint,
"DLEQ",
auxRand
)
auxRand)
}
/** Computes the challenge hash value for dleqProve as specified in
@ -58,13 +54,11 @@ object DLEQUtil {
r1: ECPublicKey,
r2: ECPublicKey,
p1: ECPublicKey,
p2: ECPublicKey
): ByteVector = {
p2: ECPublicKey): ByteVector = {
CryptoUtil
.sha256DLEQ(
p1.bytes ++ adaptorPoint.bytes ++
p2.bytes ++ r1.bytes ++ r2.bytes
)
p2.bytes ++ r1.bytes ++ r2.bytes)
.bytes
}
@ -76,8 +70,7 @@ object DLEQUtil {
def dleqProve(
fe: FieldElement,
adaptorPoint: ECPublicKey,
auxRand: ByteVector
): (FieldElement, FieldElement) = {
auxRand: ByteVector): (FieldElement, FieldElement) = {
require(!fe.isZero, "Input field element cannot be zero.")
// (fe*G, fe*Y)
@ -120,8 +113,7 @@ object DLEQUtil {
e: FieldElement,
p1: ECPublicKey,
adaptor: ECPublicKey,
p2: ECPublicKey
): Boolean = {
p2: ECPublicKey): Boolean = {
val r1 = p1.multiply(e.negate).add(s.getPublicKey)
val r2 = p2.multiply(e.negate).add(adaptor.multiply(s))
val challengeHash = dleqChallengeHash(adaptor, r1, r2, p1, p2)

View File

@ -3,10 +3,8 @@ package org.bitcoins.crypto
import scodec.bits.ByteVector
case class ECAdaptorSignature(bytes: ByteVector) extends NetworkElement {
require(
bytes.length == 162,
s"Adaptor signature must be 162 bytes, got $bytes"
)
require(bytes.length == 162,
s"Adaptor signature must be 162 bytes, got $bytes")
val (adaptedSig: ByteVector, dleqProof: ByteVector) = bytes.splitAt(98)
@ -31,8 +29,7 @@ object ECAdaptorSignature extends Factory[ECAdaptorSignature] {
untweakedNonce: ECPublicKey,
adaptedS: FieldElement,
dleqProofE: FieldElement,
dleqProofS: FieldElement
): ECAdaptorSignature = {
dleqProofS: FieldElement): ECAdaptorSignature = {
fromBytes(
tweakedNonce.bytes ++ untweakedNonce.bytes ++
adaptedS.bytes ++ dleqProofE.bytes ++ dleqProofS.bytes

View File

@ -87,10 +87,8 @@ sealed abstract class ECDigitalSignature extends NetworkElement {
}
def appendHashType(hashType: HashType): ECDigitalSignature = {
require(
this.hashTypeOpt.isEmpty,
"Cannot append HashType to signature which already has HashType"
)
require(this.hashTypeOpt.isEmpty,
"Cannot append HashType to signature which already has HashType")
val bytesWithHashType = bytes.:+(hashType.byte)
ECDigitalSignature.fromBytes(bytesWithHashType)
@ -156,10 +154,8 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
val sigWithExtra = fromBytes(bytes)
val sig = fromRS(sigWithExtra.r, sigWithExtra.s)
require(
bytes.startsWith(sig.bytes),
s"Received weirdly encoded signature at beginning of $bytes"
)
require(bytes.startsWith(sig.bytes),
s"Received weirdly encoded signature at beginning of $bytes")
sig
}
@ -170,8 +166,7 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
def fromFrontOfBytesWithSigHash(bytes: ByteVector): ECDigitalSignature = {
val sigWithoutSigHash = fromFrontOfBytes(bytes)
ECDigitalSignature(
sigWithoutSigHash.bytes :+ bytes.drop(sigWithoutSigHash.byteSize).head
)
sigWithoutSigHash.bytes :+ bytes.drop(sigWithoutSigHash.byteSize).head)
}
def apply(r: BigInt, s: BigInt): ECDigitalSignature = fromRS(r, s)
@ -195,13 +190,10 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
val totalSize = 4 + rsSize
val bytes: ByteVector = {
ByteVector(
Array(
0x30.toByte,
Array(0x30.toByte,
totalSize.toByte,
0x2.toByte,
r.toByteArray.length.toByte
)
)
r.toByteArray.length.toByte))
.++(ByteVector(r.toByteArray))
.++(ByteVector(Array(0x2.toByte, s.toByteArray.length.toByte)))
.++(ByteVector(s.toByteArray))
@ -233,8 +225,7 @@ object ECDigitalSignature extends Factory[ECDigitalSignature] {
def fromRS(byteVector: ByteVector): ECDigitalSignature = {
require(
byteVector.length == 64,
s"Incorrect size for reading a ECDigital signature from a bytevec, got ${byteVector.length}"
)
s"Incorrect size for reading a ECDigital signature from a bytevec, got ${byteVector.length}")
val r = BigInt(1, byteVector.take(32).toArray)
val s = BigInt(1, byteVector.takeRight(32).toArray)
fromRS(r, s)

View File

@ -172,8 +172,7 @@ case class ECPrivateKey(bytes: ByteVector)
override def signWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature = {
entropy: ByteVector): ECDigitalSignature = {
CryptoUtil.signWithEntropy(this, bytes, entropy)
}
@ -184,36 +183,31 @@ case class ECPrivateKey(bytes: ByteVector)
def schnorrSign(
dataToSign: ByteVector,
auxRand: ByteVector
): SchnorrDigitalSignature = {
auxRand: ByteVector): SchnorrDigitalSignature = {
CryptoUtil.schnorrSign(dataToSign, this, auxRand)
}
def schnorrSignWithNonce(
dataToSign: ByteVector,
nonce: ECPrivateKey
): SchnorrDigitalSignature = {
nonce: ECPrivateKey): SchnorrDigitalSignature = {
CryptoUtil.schnorrSignWithNonce(dataToSign, this, nonce)
}
override def adaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature = {
auxRand: ByteVector): ECAdaptorSignature = {
CryptoUtil.adaptorSign(this, adaptorPoint, msg, auxRand)
}
def completeAdaptorSignature(
adaptorSignature: ECAdaptorSignature
): ECDigitalSignature = {
adaptorSignature: ECAdaptorSignature): ECDigitalSignature = {
CryptoUtil.adaptorComplete(this, adaptorSignature)
}
def completeAdaptorSignature(
adaptorSignature: ECAdaptorSignature,
hashTypeByte: Byte
): ECDigitalSignature = {
hashTypeByte: Byte): ECDigitalSignature = {
val completedSig = completeAdaptorSignature(adaptorSignature)
ECDigitalSignature(completedSig.bytes ++ ByteVector.fromByte(hashTypeByte))
}
@ -236,8 +230,7 @@ case class ECPrivateKey(bytes: ByteVector)
// CryptoParams.curve.getN
private val N: BigInteger = new BigInteger(
"115792089237316195423570985008687907852837564279074904382605163141518161494337"
)
"115792089237316195423570985008687907852837564279074904382605163141518161494337")
def negate: ECPrivateKey = {
val negPrivKeyNum = N.subtract(new BigInteger(1, bytes.toArray))
@ -281,8 +274,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
} else
throw new IllegalArgumentException(
"Private keys must be 32 in size, got: " +
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size
)
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
def fromFieldElement(fieldElement: FieldElement): ECPrivateKey = {
@ -341,15 +333,13 @@ case class ECPublicKey(bytes: ByteVector)
def schnorrVerify(
data: ByteVector,
signature: SchnorrDigitalSignature
): Boolean = {
signature: SchnorrDigitalSignature): Boolean = {
schnorrPublicKey.verify(data, signature)
}
def schnorrComputePoint(
data: ByteVector,
nonce: SchnorrNonce
): ECPublicKey = {
nonce: SchnorrNonce): ECPublicKey = {
schnorrPublicKey.computeSigPoint(data, nonce)
}
@ -360,15 +350,13 @@ case class ECPublicKey(bytes: ByteVector)
def adaptorVerify(
msg: ByteVector,
adaptorPoint: ECPublicKey,
adaptorSignature: ECAdaptorSignature
): Boolean = {
adaptorSignature: ECAdaptorSignature): Boolean = {
CryptoUtil.adaptorVerify(adaptorSignature, this, msg, adaptorPoint)
}
def extractAdaptorSecret(
adaptorSignature: ECAdaptorSignature,
signature: ECDigitalSignature
): ECPrivateKey = {
signature: ECDigitalSignature): ECPrivateKey = {
CryptoUtil.extractAdaptorSecret(signature, adaptorSignature, this)
}

View File

@ -16,8 +16,7 @@ case class FieldElement(bytes: ByteVector)
require(
privKeyT.isSuccess || isZero,
s"$bytes is not a valid field element: ${privKeyT.failed.get.getMessage}"
)
s"$bytes is not a valid field element: ${privKeyT.failed.get.getMessage}")
def toPrivateKey: ECPrivateKey =
if (!isZero) {

View File

@ -6,16 +6,13 @@ import java.math.BigInteger
abstract class FiniteFieldMember[F <: FiniteFieldMember[F]](
fieldOrder: BigInteger,
byteSize: Int
) extends NetworkElement {
require(
bytes.length == byteSize,
s"Finite field member must have $byteSize bytes, got $bytes"
)
byteSize: Int)
extends NetworkElement {
require(bytes.length == byteSize,
s"Finite field member must have $byteSize bytes, got $bytes")
require(
toBigInteger.compareTo(fieldOrder) < 0,
s"$bytes is not a valid field member (was not less than $fieldOrder)."
)
s"$bytes is not a valid field member (was not less than $fieldOrder).")
def isZero: Boolean = bytes.toArray.forall(_ == 0.toByte)
@ -56,8 +53,8 @@ abstract class FiniteFieldMember[F <: FiniteFieldMember[F]](
abstract class FiniteFieldObject[F <: FiniteFieldMember[F]](
fieldOrder: BigInteger,
byteSize: Int
) extends Factory[F] {
byteSize: Int)
extends Factory[F] {
def fieldMemberConstructor(bytes: ByteVector): F
@ -70,8 +67,7 @@ abstract class FiniteFieldObject[F <: FiniteFieldMember[F]](
fieldMemberConstructor(bytes.tail)
} else {
throw new IllegalArgumentException(
s"Field element cannot have more than 32 bytes, got $bytes"
)
s"Field element cannot have more than 32 bytes, got $bytes")
}
}

View File

@ -68,11 +68,9 @@ object Sha256Digest extends Factory[Sha256Digest] {
}
override def fromBytes(bytes: ByteVector): Sha256Digest = {
require(
bytes.length == 32,
require(bytes.length == 32,
// $COVERAGE-OFF$
"Sha256Digest must be 32 bytes in size, got: " + bytes.length
)
"Sha256Digest must be 32 bytes in size, got: " + bytes.length)
Sha256DigestImpl(bytes)
}
@ -98,11 +96,9 @@ object Sha256DigestBE extends Factory[Sha256DigestBE] {
}
override def fromBytes(bytes: ByteVector): Sha256DigestBE = {
require(
bytes.length == 32,
require(bytes.length == 32,
// $COVERAGE-OFF$
"Sha256Digest must be 32 bytes in size, got: " + bytes.length
)
"Sha256Digest must be 32 bytes in size, got: " + bytes.length)
Sha256DigestBEImpl(bytes)
}
@ -112,10 +108,8 @@ object Sha256DigestBE extends Factory[Sha256DigestBE] {
/** Represents the result of SHA256(SHA256())
*/
case class DoubleSha256Digest(bytes: ByteVector) extends HashDigest {
require(
bytes.length == 32,
"DoubleSha256Digest must always be 32 bytes, got: " + bytes.length
)
require(bytes.length == 32,
"DoubleSha256Digest must always be 32 bytes, got: " + bytes.length)
lazy val flip: DoubleSha256DigestBE = DoubleSha256DigestBE(bytes.reverse)
@ -139,10 +133,8 @@ object DoubleSha256Digest extends Factory[DoubleSha256Digest] {
/** The big endian version of [[DoubleSha256Digest DoubleSha256Digest]] */
case class DoubleSha256DigestBE(bytes: ByteVector) extends HashDigest {
require(
bytes.length == 32,
"DoubleSha256Digest must always be 32 bytes, got: " + bytes.length
)
require(bytes.length == 32,
"DoubleSha256Digest must always be 32 bytes, got: " + bytes.length)
def flip: DoubleSha256Digest =
DoubleSha256Digest.fromBytes(bytes.reverse)
@ -177,11 +169,9 @@ object RipeMd160Digest extends Factory[RipeMd160Digest] {
}
override def fromBytes(bytes: ByteVector): RipeMd160Digest = {
require(
bytes.length == 20,
require(bytes.length == 20,
// $COVERAGE-OFF$
"RIPEMD160Digest must always be 20 bytes, got: " + bytes.length
)
"RIPEMD160Digest must always be 20 bytes, got: " + bytes.length)
RipeMd160DigestImpl(bytes)
}
}
@ -202,11 +192,9 @@ object RipeMd160DigestBE extends Factory[RipeMd160DigestBE] {
}
override def fromBytes(bytes: ByteVector): RipeMd160DigestBE = {
require(
bytes.length == 20,
require(bytes.length == 20,
// $COVERAGE-OFF$
"RIPEMD160Digest must always be 20 bytes, got: " + bytes.length
)
"RIPEMD160Digest must always be 20 bytes, got: " + bytes.length)
RipeMd160DigestBEImpl(bytes)
}
}
@ -229,11 +217,9 @@ object Sha256Hash160Digest extends Factory[Sha256Hash160Digest] {
}
override def fromBytes(bytes: ByteVector): Sha256Hash160Digest = {
require(
bytes.length == 20,
require(bytes.length == 20,
// $COVERAGE-OFF$
"Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length
)
"Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length)
Sha256Hash160DigestImpl(bytes)
}
}
@ -254,11 +240,9 @@ object Sha256Hash160DigestBE extends Factory[Sha256Hash160DigestBE] {
}
override def fromBytes(bytes: ByteVector): Sha256Hash160DigestBE = {
require(
bytes.length == 20,
require(bytes.length == 20,
// $COVERAGE-OFF$
"Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length
)
"Sha256Hash160Digest must always be 20 bytes, got: " + bytes.length)
Sha256Hash160DigestBEImpl(bytes)
}
}
@ -279,11 +263,9 @@ object Sha3_256Digest extends Factory[Sha3_256Digest] {
}
override def fromBytes(bytes: ByteVector): Sha3_256Digest = {
require(
bytes.length == 32,
require(bytes.length == 32,
// $COVERAGE-OFF$
"Sha3-256Digest must be 32 bytes in size, got: " + bytes.length
)
"Sha3-256Digest must be 32 bytes in size, got: " + bytes.length)
Sha3_256DigestImpl(bytes)
}
@ -309,11 +291,9 @@ object Sha3_256DigestBE extends Factory[Sha3_256DigestBE] {
}
override def fromBytes(bytes: ByteVector): Sha3_256DigestBE = {
require(
bytes.length == 32,
require(bytes.length == 32,
// $COVERAGE-OFF$
"Sha3-256Digest must be 32 bytes in size, got: " + bytes.length
)
"Sha3-256Digest must be 32 bytes in size, got: " + bytes.length)
Sha3_256DigestBEImpl(bytes)
}

View File

@ -107,16 +107,14 @@ object HashType extends Factory[HashType] {
false
}
lazy val hashTypes = Seq(
sigHashAll,
lazy val hashTypes = Seq(sigHashAll,
sigHashNone,
sigHashSingle,
sigHashAnyoneCanPay,
sigHashNoneAnyoneCanPay,
sigHashAllAnyoneCanPay,
sigHashSingleAnyoneCanPay,
sigHashDefault
)
sigHashDefault)
lazy val hashTypeBytes: Vector[Byte] = Vector(
sigHashDefaultByte,
@ -157,8 +155,7 @@ object HashType extends Factory[HashType] {
val sigHashAnyoneCanPayByte = 0x80.toByte
val sigHashAnyoneCanPay: SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY(
sigHashAnyoneCanPayNum
)
sigHashAnyoneCanPayNum)
/** The default byte for [[SIGHASH_NONE]] */
val sigHashNoneByte: Byte = 2.toByte
@ -176,8 +173,7 @@ object HashType extends Factory[HashType] {
val sigHashAllAnyoneCanPayNum = sigHashAllByte.toInt | sigHashAnyoneCanPayNum
val sigHashAllAnyoneCanPay = SIGHASH_ALL_ANYONECANPAY(
sigHashAllAnyoneCanPayNum
)
sigHashAllAnyoneCanPayNum)
val sigHashNoneAnyoneCanPayByte =
(HashType.sigHashNoneByte | HashType.sigHashAnyoneCanPayByte).toByte
@ -186,8 +182,7 @@ object HashType extends Factory[HashType] {
sigHashNoneByte.toInt | sigHashAnyoneCanPayNum
val sigHashNoneAnyoneCanPay = SIGHASH_NONE_ANYONECANPAY(
sigHashNoneAnyoneCanPayNum
)
sigHashNoneAnyoneCanPayNum)
val sigHashSingleAnyoneCanPayByte =
(HashType.sigHashSingleByte | HashType.sigHashAnyoneCanPayByte).toByte
@ -196,8 +191,7 @@ object HashType extends Factory[HashType] {
sigHashSingleByte.toInt | sigHashAnyoneCanPayNum
val sigHashSingleAnyoneCanPay = SIGHASH_SINGLE_ANYONECANPAY(
sigHashSingleAnyoneCanPayNum
)
sigHashSingleAnyoneCanPayNum)
/** Checks if the given digital signature has a valid hash type Mimics this
* functionality inside of Bitcoin Core
@ -220,8 +214,7 @@ case class SIGHASH_ALL(override val num: Int) extends HashType {
HashType.isSigHashAll(num),
"SIGHASH_ALL acts as a 'catch-all' for undefined hashtypes, and has a default " +
"value of one. Your input was: " + num + ", which is of hashType: " + HashType(
num
)
num)
)
}
@ -230,43 +223,32 @@ object SIGHASH_ALL {
}
case class SIGHASH_NONE(override val num: Int) extends HashType {
require(
HashType.isSigHashNone(num),
"The given number is not a SIGHASH_NONE number: " + num
)
require(HashType.isSigHashNone(num),
"The given number is not a SIGHASH_NONE number: " + num)
}
case class SIGHASH_SINGLE(override val num: Int) extends HashType {
require(
HashType.isSigHashSingle(num),
"The given number is not a SIGHASH_SINGLE number: " + num
)
require(HashType.isSigHashSingle(num),
"The given number is not a SIGHASH_SINGLE number: " + num)
}
case class SIGHASH_ANYONECANPAY(override val num: Int) extends HashType {
require(
HashType.isSigHashAnyoneCanPay(num),
"The given number was not a SIGHASH_ANYONECANPAY number: " + num
)
require(HashType.isSigHashAnyoneCanPay(num),
"The given number was not a SIGHASH_ANYONECANPAY number: " + num)
}
case class SIGHASH_ALL_ANYONECANPAY(override val num: Int) extends HashType {
require(
HashType.isSigHashAllAnyoneCanPay(num),
"The given number was not a SIGHASH_ALL_ANYONECANPAY number: " + num
)
require(HashType.isSigHashAllAnyoneCanPay(num),
"The given number was not a SIGHASH_ALL_ANYONECANPAY number: " + num)
}
case class SIGHASH_NONE_ANYONECANPAY(override val num: Int) extends HashType {
require(
HashType.isSigHashNoneAnyoneCanPay(num),
"The given number was not a SIGHASH_NONE_ANYONECANPAY number: " + num
)
require(HashType.isSigHashNoneAnyoneCanPay(num),
"The given number was not a SIGHASH_NONE_ANYONECANPAY number: " + num)
}
case class SIGHASH_SINGLE_ANYONECANPAY(override val num: Int) extends HashType {
require(
HashType.isSigHashSingleAnyoneCanPay(num),
"The given number was not a SIGHASH_SINGLE_ANYONECANPAY number: " + num
)
"The given number was not a SIGHASH_SINGLE_ANYONECANPAY number: " + num)
}

View File

@ -12,19 +12,13 @@ object SchnorrDigitalSignature extends Factory[SchnorrDigitalSignature] {
// If the sig is 65 bytes long, return sig[64] 0x00[20] and
// Verify(q, hashTapSighash(0x00 || SigMsg(sig[64], 0)), sig[0:64]).
override def fromBytes(bytes: ByteVector): SchnorrDigitalSignature = {
require(
bytes.length == 64,
s"SchnorrDigitalSignature must be exactly 64 bytes, got $bytes"
)
SchnorrDigitalSignature(
SchnorrNonce(bytes.take(32)),
FieldElement(bytes.drop(32))
)
require(bytes.length == 64,
s"SchnorrDigitalSignature must be exactly 64 bytes, got $bytes")
SchnorrDigitalSignature(SchnorrNonce(bytes.take(32)),
FieldElement(bytes.drop(32)))
}
lazy val dummy: SchnorrDigitalSignature =
SchnorrDigitalSignature(
FieldElement.one.getPublicKey.schnorrNonce,
FieldElement.one
)
SchnorrDigitalSignature(FieldElement.one.getPublicKey.schnorrNonce,
FieldElement.one)
}

View File

@ -25,8 +25,7 @@ object SchnorrNonce extends Factory[SchnorrNonce] {
def kFromBipSchnorr(
privKey: ECPrivateKey,
message: ByteVector,
auxRand: ByteVector
): ECPrivateKey = {
auxRand: ByteVector): ECPrivateKey = {
val privKeyForUse = privKey.schnorrKey
val randHash =
@ -34,8 +33,7 @@ object SchnorrNonce extends Factory[SchnorrNonce] {
val maskedKey = randHash.xor(privKeyForUse.bytes)
val nonceHash = CryptoUtil.sha256SchnorrNonce(
maskedKey ++ privKey.schnorrPublicKey.bytes ++ message
)
maskedKey ++ privKey.schnorrPublicKey.bytes ++ message)
ECPrivateKey(nonceHash.bytes).nonceKey
}
@ -46,8 +44,7 @@ object SchnorrNonce extends Factory[SchnorrNonce] {
def fromBipSchnorr(
privKey: ECPrivateKey,
message: ByteVector,
auxRand: ByteVector
): SchnorrNonce = {
auxRand: ByteVector): SchnorrNonce = {
val k = kFromBipSchnorr(privKey, message, auxRand)
k.publicKey.schnorrNonce
}

View File

@ -6,14 +6,10 @@ import scala.annotation.tailrec
import scala.util.Try
case class SchnorrPublicKey(bytes: ByteVector) extends PublicKey {
require(
bytes.length == 32,
s"Schnorr public keys must be 32 bytes, got $bytes"
)
require(
Try(publicKey).isSuccess,
s"Schnorr public key must be a valid x coordinate, got $bytes"
)
require(bytes.length == 32,
s"Schnorr public keys must be 32 bytes, got $bytes")
require(Try(publicKey).isSuccess,
s"Schnorr public key must be a valid x coordinate, got $bytes")
def verify(data: ByteVector, signature: SchnorrDigitalSignature): Boolean = {
CryptoUtil.schnorrVerify(data, this, signature)
@ -33,8 +29,7 @@ case class SchnorrPublicKey(bytes: ByteVector) extends PublicKey {
def computeSigPoint(
bytesToHash: Vector[ByteVector],
nonces: Vector[SchnorrNonce]
): ECPublicKey = {
nonces: Vector[SchnorrNonce]): ECPublicKey = {
// TODO: when combine function is ported from secp, use that instead for nonces
val bytesAndNonces = bytesToHash.zip(nonces)
@ -43,8 +38,7 @@ case class SchnorrPublicKey(bytes: ByteVector) extends PublicKey {
.sha256SchnorrChallenge(
nonce.bytes ++ this.bytes ++ CryptoUtil
.sha256DLCAttestation(bytes)
.bytes
)
.bytes)
.bytes
val e = ECPrivateKey(eBytes)
(e, nonce.publicKey)
@ -62,8 +56,7 @@ case class SchnorrPublicKey(bytes: ByteVector) extends PublicKey {
def computeSigPoint(
data: ByteVector,
nonce: SchnorrNonce,
compressed: Boolean
): ECPublicKey = {
compressed: Boolean): ECPublicKey = {
CryptoUtil.schnorrComputeSigPoint(data, nonce, this, compressed)
}
@ -84,10 +77,8 @@ object SchnorrPublicKey extends Factory[SchnorrPublicKey] {
@tailrec
def fromBytes(bytes: ByteVector): SchnorrPublicKey = {
require(
bytes.length <= 33,
s"XOnlyPublicKey must be less than 33 bytes, got $bytes"
)
require(bytes.length <= 33,
s"XOnlyPublicKey must be less than 33 bytes, got $bytes")
if (bytes.length == 32)
new SchnorrPublicKey(bytes)
else if (bytes.length < 32) {
@ -99,8 +90,7 @@ object SchnorrPublicKey extends Factory[SchnorrPublicKey] {
} else {
throw new IllegalArgumentException(
"XOnlyPublicKey cannot be greater than 33 bytes in size, got: " +
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size
)
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
}

View File

@ -51,10 +51,8 @@ case class SecpPointFinite(x: CurveCoordinate, y: CurveCoordinate)
def schnorrNonce: SchnorrNonce = {
val pub = toPublicKey
require(
pub.isCompressed,
s"SchnorrNonce can only be created from compressed public keys"
)
require(pub.isCompressed,
s"SchnorrNonce can only be created from compressed public keys")
pub.schnorrNonce
}
}
@ -70,10 +68,8 @@ object SecpPoint {
SecpPointFinite(CurveCoordinate.fromBytes(x), CurveCoordinate.fromBytes(y))
def apply(x: Array[Byte], y: Array[Byte]): SecpPointFinite =
SecpPointFinite(
CurveCoordinate.fromByteArray(x),
CurveCoordinate.fromByteArray(y)
)
SecpPointFinite(CurveCoordinate.fromByteArray(x),
CurveCoordinate.fromByteArray(y))
def apply(x: BigInteger, y: BigInteger): SecpPointFinite =
SecpPointFinite(CurveCoordinate(x), CurveCoordinate(y))

View File

@ -36,12 +36,10 @@ trait AsyncSign {
*/
def asyncSignWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): Future[ECDigitalSignature]
entropy: ByteVector): Future[ECDigitalSignature]
private def asyncSignLowR(bytes: ByteVector, startAt: Long)(implicit
ec: ExecutionContext
): Future[ECDigitalSignature] = {
ec: ExecutionContext): Future[ECDigitalSignature] = {
val sigF = if (startAt == 0) { // On first try, use normal signing
asyncSign(bytes)
} else { // Subsequently, use additional entropy
@ -58,9 +56,8 @@ trait AsyncSign {
}
}
def asyncSignLowR(
bytes: ByteVector
)(implicit ec: ExecutionContext): Future[ECDigitalSignature] = {
def asyncSignLowR(bytes: ByteVector)(implicit
ec: ExecutionContext): Future[ECDigitalSignature] = {
asyncSignLowR(bytes, startAt = 0)
}
@ -73,10 +70,9 @@ object AsyncSign {
asyncSignFunction: ByteVector => Future[ECDigitalSignature],
asyncSignWithEntropyFunction: (
ByteVector,
ByteVector
) => Future[ECDigitalSignature],
override val publicKey: ECPublicKey
) extends AsyncSign {
ByteVector) => Future[ECDigitalSignature],
override val publicKey: ECPublicKey)
extends AsyncSign {
override def asyncSign(bytes: ByteVector): Future[ECDigitalSignature] = {
asyncSignFunction(bytes)
@ -84,8 +80,7 @@ object AsyncSign {
override def asyncSignWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): Future[ECDigitalSignature] = {
entropy: ByteVector): Future[ECDigitalSignature] = {
asyncSignWithEntropyFunction(bytes, entropy)
}
}
@ -94,19 +89,15 @@ object AsyncSign {
asyncSign: ByteVector => Future[ECDigitalSignature],
asyncSignWithEntropy: (
ByteVector,
ByteVector
) => Future[ECDigitalSignature],
pubKey: ECPublicKey
): AsyncSign = {
ByteVector) => Future[ECDigitalSignature],
pubKey: ECPublicKey): AsyncSign = {
AsyncSignImpl(asyncSign, asyncSignWithEntropy, pubKey)
}
def constant(sig: ECDigitalSignature, pubKey: ECPublicKey): AsyncSign = {
AsyncSignImpl(
_ => Future.successful(sig),
AsyncSignImpl(_ => Future.successful(sig),
(_, _) => Future.successful(sig),
pubKey
)
pubKey)
}
/** This dummySign function is useful for the case where we do not have the
@ -127,13 +118,11 @@ trait AsyncAdaptorSign extends AsyncSign {
def asyncAdaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): Future[ECAdaptorSignature]
auxRand: ByteVector): Future[ECAdaptorSignature]
def asyncAdaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector
): Future[ECAdaptorSignature] = {
msg: ByteVector): Future[ECAdaptorSignature] = {
val auxRand = ECPrivateKey.freshPrivateKey.bytes
asyncAdaptorSign(adaptorPoint, msg, auxRand)
}
@ -158,13 +147,11 @@ trait Sign extends AsyncSign {
*/
def signWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature
entropy: ByteVector): ECDigitalSignature
override def asyncSignWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): Future[ECDigitalSignature] = {
entropy: ByteVector): Future[ECDigitalSignature] = {
Future.successful(signWithEntropy(bytes, entropy))
}
@ -193,9 +180,8 @@ trait Sign extends AsyncSign {
signLowR(bytes, startAt = 0)
}
override def asyncSignLowR(
bytes: ByteVector
)(implicit ec: ExecutionContext): Future[ECDigitalSignature] = {
override def asyncSignLowR(bytes: ByteVector)(implicit
ec: ExecutionContext): Future[ECDigitalSignature] = {
Future.successful(signLowR(bytes))
}
}
@ -205,8 +191,8 @@ object Sign {
private case class SignImpl(
signFunction: ByteVector => ECDigitalSignature,
signWithEntropyFunction: (ByteVector, ByteVector) => ECDigitalSignature,
override val publicKey: ECPublicKey
) extends Sign {
override val publicKey: ECPublicKey)
extends Sign {
override def sign(bytes: ByteVector): ECDigitalSignature = {
signFunction(bytes)
@ -214,8 +200,7 @@ object Sign {
override def signWithEntropy(
bytes: ByteVector,
entropy: ByteVector
): ECDigitalSignature = {
entropy: ByteVector): ECDigitalSignature = {
signWithEntropyFunction(bytes, entropy)
}
}
@ -223,8 +208,7 @@ object Sign {
def apply(
sign: ByteVector => ECDigitalSignature,
signWithEntropy: (ByteVector, ByteVector) => ECDigitalSignature,
pubKey: ECPublicKey
): Sign = {
pubKey: ECPublicKey): Sign = {
SignImpl(sign, signWithEntropy, pubKey)
}
@ -242,13 +226,11 @@ trait AdaptorSign extends Sign with AsyncAdaptorSign {
def adaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): ECAdaptorSignature
auxRand: ByteVector): ECAdaptorSignature
def adaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector
): ECAdaptorSignature = {
msg: ByteVector): ECAdaptorSignature = {
val auxRand = ECPrivateKey.freshPrivateKey.bytes
adaptorSign(adaptorPoint, msg, auxRand)
}
@ -256,8 +238,7 @@ trait AdaptorSign extends Sign with AsyncAdaptorSign {
override def asyncAdaptorSign(
adaptorPoint: ECPublicKey,
msg: ByteVector,
auxRand: ByteVector
): Future[ECAdaptorSignature] = {
auxRand: ByteVector): Future[ECAdaptorSignature] = {
Future.successful(adaptorSign(adaptorPoint, msg, auxRand))
}
}

View File

@ -3,8 +3,6 @@ package org.bitcoins.crypto
import scodec.bits.ByteVector
case class SipHashKey(bytes: ByteVector) extends NetworkElement {
require(
bytes.size == 16,
"Can only use a key length of 16 bytes, got: " + bytes.size
)
require(bytes.size == 16,
"Can only use a key length of 16 bytes, got: " + bytes.size)
}

View File

@ -9,14 +9,10 @@ import scala.util.Try
* y-coordinate parity
*/
case class XOnlyPubKey(bytes: ByteVector) extends PublicKey {
require(
bytes.length == 32,
s"x-only public keys must be 32 bytes, got $bytes"
)
require(
Try(publicKey(EvenParity)).isSuccess,
s"x-only public key must be a valid x coordinate, got $bytes"
)
require(bytes.length == 32,
s"x-only public keys must be 32 bytes, got $bytes")
require(Try(publicKey(EvenParity)).isSuccess,
s"x-only public key must be a valid x coordinate, got $bytes")
def publicKey(parity: KeyParity): ECPublicKey = {
val pubKeyBytes = parity.bytes ++ bytes
@ -48,8 +44,7 @@ case class XOnlyPubKey(bytes: ByteVector) extends PublicKey {
def checkTapTweak(
internal: XOnlyPubKey,
merkleRootOpt: Option[Sha256Digest],
parity: Boolean
): Boolean = {
parity: Boolean): Boolean = {
// Q = point_add(lift_x(pubkey), point_mul(G, t))
val tweaked = internal.computeTapTweakHash(merkleRootOpt)
val fe = FieldElement.fromBytes(tweaked.bytes)
@ -84,8 +79,7 @@ case class XOnlyPubKey(bytes: ByteVector) extends PublicKey {
* @return
*/
def createTapTweak(
merkleRootOpt: Option[Sha256Digest]
): (KeyParity, XOnlyPubKey) = {
merkleRootOpt: Option[Sha256Digest]): (KeyParity, XOnlyPubKey) = {
val taggedHash = computeTapTweakHash(merkleRootOpt)
val result = CryptoParams.getG
.multiply(FieldElement(taggedHash.bytes))
@ -102,10 +96,8 @@ object XOnlyPubKey extends Factory[XOnlyPubKey] {
@tailrec
def fromBytes(bytes: ByteVector): XOnlyPubKey = {
require(
bytes.length <= 33,
s"XOnlyPublicKey must be less than 33 bytes, got $bytes"
)
require(bytes.length <= 33,
s"XOnlyPublicKey must be less than 33 bytes, got $bytes")
if (bytes.length == 32)
new XOnlyPubKey(bytes)
@ -118,8 +110,7 @@ object XOnlyPubKey extends Factory[XOnlyPubKey] {
} else {
throw new IllegalArgumentException(
"XOnlyPublicKey cannot be greater than 33 bytes in size, got: " +
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size
)
CryptoBytesUtil.encodeHex(bytes) + " which is of size: " + bytes.size)
}
}

View File

@ -89,8 +89,7 @@ object KeySet {
def apply(
keys: Vector[SchnorrPublicKey],
tweaks: Vector[MuSigTweak]
): LexicographicKeySet = {
tweaks: Vector[MuSigTweak]): LexicographicKeySet = {
val sortedKeys = keys.sorted(NetworkElement.lexicographicalOrdering)
LexicographicKeySet(sortedKeys, tweaks)
}
@ -99,13 +98,11 @@ object KeySet {
/** The default way of ordering a KeySet is lexicographically */
case class LexicographicKeySet(
override val keys: Vector[SchnorrPublicKey],
override val tweaks: Vector[MuSigTweak] = Vector.empty
) extends KeySet {
override val tweaks: Vector[MuSigTweak] = Vector.empty)
extends KeySet {
keys.init.zip(keys.tail).foreach { case (key1, key2) =>
require(
key1.hex.compareTo(key2.hex) <= 0,
"Keys must be sorted lexicographically"
)
require(key1.hex.compareTo(key2.hex) <= 0,
"Keys must be sorted lexicographically")
}
}
@ -114,5 +111,5 @@ case class LexicographicKeySet(
*/
case class UnsortedKeySet(
override val keys: Vector[SchnorrPublicKey],
override val tweaks: Vector[MuSigTweak] = Vector.empty
) extends KeySet
override val tweaks: Vector[MuSigTweak] = Vector.empty)
extends KeySet

View File

@ -6,10 +6,8 @@ import scodec.bits.ByteVector
/** Wraps the ephemeral private keys making up a MuSig2 nonce */
case class MuSigNoncePriv(privNonces: Vector[ECPrivateKey])
extends NetworkElement {
require(
privNonces.length == MuSigUtil.nonceNum,
s"Exactly ${MuSigUtil.nonceNum} keys are expected, found $privNonces"
)
require(privNonces.length == MuSigUtil.nonceNum,
s"Exactly ${MuSigUtil.nonceNum} keys are expected, found $privNonces")
def toPublicNonces: MuSigNoncePub = {
MuSigNoncePub(privNonces.map(_.publicKey.toPoint))
@ -31,13 +29,11 @@ case class MuSigNoncePriv(privNonces: Vector[ECPrivateKey])
/** Collapses this into a single ephemeral private key */
def sumToKey(b: FieldElement): FieldElement = {
MuSigUtil.nonceSum[FieldElement](
toFieldElements,
MuSigUtil.nonceSum[FieldElement](toFieldElements,
b,
_.add(_),
_.multiply(_),
FieldElement.zero
)
FieldElement.zero)
}
}
@ -61,25 +57,18 @@ object MuSigNoncePriv extends Factory[MuSigNoncePriv] {
privKeyOpt: Option[ECPrivateKey] = None,
aggPubKeyOpt: Option[SchnorrPublicKey] = None,
msgOpt: Option[ByteVector] = None,
extraInOpt: Option[ByteVector] = None
): MuSigNoncePriv = {
require(
preRand.length == 32,
s"32 bytes of entropy must be provided, found $preRand"
)
require(
msgOpt.forall(msg => msg.length == 32),
s"The message to be signed must be 32 bytes, found $msgOpt"
)
extraInOpt: Option[ByteVector] = None): MuSigNoncePriv = {
require(preRand.length == 32,
s"32 bytes of entropy must be provided, found $preRand")
require(msgOpt.forall(msg => msg.length == 32),
s"The message to be signed must be 32 bytes, found $msgOpt")
require(
extraInOpt.forall(_.length <= 4294967295L),
"extraIn too long, its length must be represented by at most four bytes"
)
"extraIn too long, its length must be represented by at most four bytes")
def serializeWithLen(
bytesOpt: Option[ByteVector],
lengthSize: Int = 1
): ByteVector = {
lengthSize: Int = 1): ByteVector = {
bytesOpt match {
case Some(bytes) =>
ByteVector.fromLong(bytes.length, lengthSize) ++ bytes
@ -115,8 +104,7 @@ object MuSigNoncePriv extends Factory[MuSigNoncePriv] {
privKeyOpt: Option[ECPrivateKey] = None,
aggPubKeyOpt: Option[SchnorrPublicKey] = None,
msgOpt: Option[ByteVector] = None,
extraInOpt: Option[ByteVector] = None
): MuSigNoncePriv = {
extraInOpt: Option[ByteVector] = None): MuSigNoncePriv = {
val preRand = CryptoUtil.randomBytes(32)
genInternal(preRand, privKeyOpt, aggPubKeyOpt, msgOpt, extraInOpt)

View File

@ -5,10 +5,8 @@ import scodec.bits.ByteVector
/** Wraps the ephemeral points making up a MuSig2 nonce */
case class MuSigNoncePub(pubNonces: Vector[SecpPoint]) extends NetworkElement {
require(
pubNonces.length == MuSigUtil.nonceNum,
s"Exactly ${MuSigUtil.nonceNum} keys are expected, found $pubNonces"
)
require(pubNonces.length == MuSigUtil.nonceNum,
s"Exactly ${MuSigUtil.nonceNum} keys are expected, found $pubNonces")
def apply(i: Int): SecpPoint = {
pubNonces(i)
@ -27,13 +25,11 @@ case class MuSigNoncePub(pubNonces: Vector[SecpPoint]) extends NetworkElement {
/** Collapses this into a single ephemeral public key */
def sumToKey(b: FieldElement): ECPublicKey = {
MuSigUtil.nonceSum[SecpPoint](
pubNonces,
MuSigUtil.nonceSum[SecpPoint](pubNonces,
b,
_.add(_),
_.multiply(_),
SecpPointInfinity
) match {
SecpPointInfinity) match {
case SecpPointInfinity => CryptoParams.getG
case p: SecpPointFinite => p.toPublicKey
}

View File

@ -7,14 +7,12 @@ import org.bitcoins.crypto.{ECPublicKey, FieldElement, OddParity}
*/
case class MuSigTweakContext(
parityAcc: ParityMultiplier,
tweakAcc: FieldElement
) {
tweakAcc: FieldElement) {
/** Adds tweak to tweakAcc and aggPubKey changing parityAcc if necessary */
def applyTweak(
tweak: MuSigTweak,
aggPubKey: ECPublicKey
): (ECPublicKey, MuSigTweakContext) = {
aggPubKey: ECPublicKey): (ECPublicKey, MuSigTweakContext) = {
val parityMult =
if (tweak.isXOnlyT && aggPubKey.parity == OddParity) Neg
else Pos

View File

@ -7,8 +7,7 @@ import org.bitcoins.crypto.{EvenParity, FieldElement, KeyParity, OddParity}
case class MuSigTweakData(
context: MuSigTweakContext,
aggPubKeyParity: KeyParity,
e: FieldElement
) {
e: FieldElement) {
def additiveTweak: FieldElement = {
val g = aggPubKeyParity match {

View File

@ -37,8 +37,7 @@ object MuSigUtil {
b: FieldElement,
add: (T, T) => T,
multiply: (T, FieldElement) => T,
identity: T
): T = {
identity: T): T = {
nonces
.foldLeft((FieldElement.one, identity)) { case ((pow, sumSoFar), nonce) =>
val prod = multiply(nonce, pow)
@ -55,8 +54,7 @@ object MuSigUtil {
aggNoncePub: MuSigNoncePub,
privKey: ECPrivateKey,
message: ByteVector,
keySet: KeySet
): (ECPublicKey, FieldElement) = {
keySet: KeySet): (ECPublicKey, FieldElement) = {
val pubKey = privKey.publicKey
val coef = keySet.keyAggCoef(pubKey.schnorrPublicKey)
val SigningSession(b, aggNonce, e) =
@ -80,18 +78,14 @@ object MuSigUtil {
val s = adjustedPrivKey.multiply(e).multiply(coef).add(privNonceSum)
require(
partialSigVerify(
s,
require(partialSigVerify(s,
noncePriv.toPublicNonces,
pubKey.schnorrPublicKey,
keySet,
b,
aggNonce,
e
),
"Failed verification when generating signature."
)
e),
"Failed verification when generating signature.")
(aggNonce, s)
}
@ -101,21 +95,16 @@ object MuSigUtil {
pubNonces: Vector[MuSigNoncePub],
keySet: KeySet,
message: ByteVector,
signerIndex: Int
): Boolean = {
require(
signerIndex >= 0 && signerIndex < keySet.length,
s"Invalid signer index $signerIndex for ${keySet.length} signers"
)
signerIndex: Int): Boolean = {
require(signerIndex >= 0 && signerIndex < keySet.length,
s"Invalid signer index $signerIndex for ${keySet.length} signers")
partialSigVerify(
partialSig,
partialSigVerify(partialSig,
pubNonces(signerIndex),
MuSigNoncePub.aggregate(pubNonces),
keySet(signerIndex),
keySet,
message
)
message)
}
def partialSigVerify(
@ -124,8 +113,7 @@ object MuSigUtil {
aggNoncePub: MuSigNoncePub,
pubKey: SchnorrPublicKey,
keySet: KeySet,
message: ByteVector
): Boolean = {
message: ByteVector): Boolean = {
val SigningSession(b, aggNonce, e) =
SigningSession(aggNoncePub, keySet, message)
@ -139,8 +127,7 @@ object MuSigUtil {
keySet: KeySet,
b: FieldElement,
aggNonce: ECPublicKey,
e: FieldElement
): Boolean = {
e: FieldElement): Boolean = {
val nonceSum = noncePub.sumToKey(b)
val nonceSumAdjusted = aggNonce.parity match {
case EvenParity => nonceSum
@ -153,8 +140,7 @@ object MuSigUtil {
val aggKey = pubKey.toXOnly.publicKey(aggKeyParity)
val a = keySet.keyAggCoef(pubKey)
partialSig.getPublicKey == nonceSumAdjusted.add(
aggKey.multiply(e.multiply(a))
)
aggKey.multiply(e.multiply(a)))
}
/** Aggregates MuSig partial signatures into a BIP340 SchnorrDigitalSignature
@ -163,8 +149,7 @@ object MuSigUtil {
sVals: Vector[FieldElement],
aggNoncePub: MuSigNoncePub,
keySet: KeySet,
message: ByteVector
): SchnorrDigitalSignature = {
message: ByteVector): SchnorrDigitalSignature = {
val SigningSession(_, aggNonce, e) =
SigningSession(aggNoncePub, keySet, message)
val tweakData =
@ -178,8 +163,7 @@ object MuSigUtil {
def signAgg(
sVals: Vector[FieldElement],
aggPubNonce: ECPublicKey,
tweakDataOpt: Option[MuSigTweakData] = None
): SchnorrDigitalSignature = {
tweakDataOpt: Option[MuSigTweakData] = None): SchnorrDigitalSignature = {
val sSum = sVals.reduce(_.add(_))
val s = tweakDataOpt match {
case Some(tweakData) => sSum.add(tweakData.additiveTweak)

View File

@ -12,16 +12,14 @@ import scodec.bits.ByteVector
case class SigningSession(
b: FieldElement,
aggNonce: ECPublicKey,
e: FieldElement
)
e: FieldElement)
object SigningSession {
def computeB(
aggNoncePub: MuSigNoncePub,
keySet: KeySet,
message: ByteVector
): FieldElement = {
message: ByteVector): FieldElement = {
val aggPubKey = keySet.aggPubKey.schnorrPublicKey
val bHash =
@ -33,12 +31,10 @@ object SigningSession {
def computeE(
aggPubKey: SchnorrPublicKey,
aggNonce: ECPublicKey,
message: ByteVector
): FieldElement = {
message: ByteVector): FieldElement = {
val eBytes = CryptoUtil
.sha256SchnorrChallenge(
aggNonce.schnorrNonce.bytes ++ aggPubKey.bytes ++ message
)
aggNonce.schnorrNonce.bytes ++ aggPubKey.bytes ++ message)
.bytes
FieldElement(new java.math.BigInteger(1, eBytes.toArray))
@ -47,8 +43,7 @@ object SigningSession {
def getSessionValues(
aggNoncePub: MuSigNoncePub,
keySet: KeySet,
message: ByteVector
): SigningSession = {
message: ByteVector): SigningSession = {
val aggPubKey = keySet.aggPubKey.schnorrPublicKey
val b = computeB(aggNoncePub, keySet, message)
val aggNonce = aggNoncePub.sumToKey(b)
@ -60,8 +55,7 @@ object SigningSession {
def apply(
aggNoncePub: MuSigNoncePub,
keySet: KeySet,
message: ByteVector
): SigningSession = {
message: ByteVector): SigningSession = {
getSessionValues(aggNoncePub, keySet, message)
}
}