JVM implementation

This commit is contained in:
Salomon BRYS
2020-06-26 13:48:50 +02:00
commit 54abe2a397
29 changed files with 5057 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2020 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.secp256k1
import kotlin.jvm.JvmStatic
import kotlin.jvm.Synchronized
public expect object Secp256k1 {
public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean
public fun sign(data: ByteArray, sec: ByteArray): ByteArray
public fun signCompact(data: ByteArray, sec: ByteArray): ByteArray
public fun secKeyVerify(seckey: ByteArray): Boolean
public fun computePubkey(seckey: ByteArray): ByteArray
public fun parsePubkey(pubkey: ByteArray): ByteArray
public fun cleanup()
public fun cloneContext(): Long
public fun privKeyNegate(privkey: ByteArray): ByteArray
public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray
public fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray
public fun pubKeyNegate(pubkey: ByteArray): ByteArray
public fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray
public fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray
public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray
public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray
public fun randomize(seed: ByteArray): Boolean
}

View File

@@ -0,0 +1,284 @@
package fr.acinq.secp256k1
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* This class holds test cases defined for testing this library.
*/
class Secp256k1Test {
//TODO improve comments/add more tests
/**
* This tests verify() for a valid signature
*/
@Test
fun testVerifyPos() {
var result: Boolean
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase())
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
result = Secp256k1.verify(data, sig, pub)
assertEquals(result, true, "testVerifyPos")
val sigCompact: ByteArray = Hex.decode("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase())
result = Secp256k1.verify(data, sigCompact, pub)
assertEquals(result, true, "testVerifyPos")
}
/**
* This tests verify() for a non-valid signature
*/
@Test
fun testVerifyNeg() {
var result: Boolean
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()) //sha256hash of "testing"
val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase())
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
result = Secp256k1.verify(data, sig, pub)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, false, "testVerifyNeg")
}
/**
* This tests secret key verify() for a valid secretkey
*/
@Test
fun testSecKeyVerifyPos() {
var result: Boolean
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
result = Secp256k1.secKeyVerify(sec)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, true, "testSecKeyVerifyPos")
}
/**
* This tests secret key verify() for an invalid secretkey
*/
@Test
fun testSecKeyVerifyNeg() {
var result: Boolean
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
result = Secp256k1.secKeyVerify(sec)
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
assertEquals(result, false, "testSecKeyVerifyNeg")
}
/**
* This tests public key create() for a valid secretkey
*/
@Test
fun testPubKeyCreatePos() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.computePubkey(sec)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
"testPubKeyCreatePos"
)
}
/**
* This tests public key create() for a invalid secretkey
*/
@Test
fun testPubKeyCreateNeg() {
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
val resultArr: ByteArray = Secp256k1.computePubkey(sec)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(pubkeyString, "", "testPubKeyCreateNeg")
}
@Test
fun testPubKeyNegatePos() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val pubkey: ByteArray = Secp256k1.computePubkey(sec)
val pubkeyString: String = Hex.encode(pubkey).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
"testPubKeyCreatePos"
)
val pubkey1: ByteArray = Secp256k1.pubKeyNegate(pubkey)
val pubkeyString1: String = Hex.encode(pubkey1).toUpperCase()
assertEquals(
pubkeyString1,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2DDEFC12B6B8E73968536514302E69ED1DDB24B999EFEE79C12D03AB17E79E1989",
"testPubKeyNegatePos"
)
}
/**
* This tests public key create() for a valid secretkey
*/
@Test
fun testPubKeyParse() {
val pub: ByteArray = Hex.decode("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D".toLowerCase())
val resultArr: ByteArray = Secp256k1.parsePubkey(pub)
val pubkeyString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
pubkeyString,
"04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6",
"testPubKeyAdd"
)
}
@Test
fun testPubKeyAdd() {
val pub1: ByteArray = Hex.decode("041b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f70beaf8f588b541507fed6a642c5ab42dfdf8120a7f639de5122d47a69a8e8d1".toLowerCase())
val pub2: ByteArray = Hex.decode("044d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d07662a3eada2d0fe208b6d257ceb0f064284662e857f57b66b54c198bd310ded36d0".toLowerCase())
val pub3: ByteArray = Secp256k1.pubKeyAdd(pub1, pub2)
val pubkeyString: String = Hex.encode(pub3).toUpperCase()
assertEquals(
pubkeyString,
"04531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE3379E92C265E71E481BA82A84675A47AC705A200FCD524E92D93B0E7386F26A5458",
"testPubKeyAdd"
)
}
/**
* This tests sign() for a valid secretkey
*/
@Test
fun testSignPos() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.sign(data, sec)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9",
"testSignPos"
)
}
/**
* This tests sign() for a invalid secretkey
*/
@Test
fun testSignNeg() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase())
val resultArr: ByteArray = Secp256k1.sign(data, sec)
val sigString: String = Hex.encode(resultArr)
assertEquals(sigString, "", "testSignNeg")
}
@Test
fun testSignCompactPos() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val resultArr: ByteArray = Secp256k1.signCompact(data, sec)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9",
"testSignCompactPos"
)
//assertEquals( sigString, "30 44 02 20 182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A2 02 20 1C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
}
@Test
fun testPrivKeyTweakNegate() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val sec1: ByteArray = Secp256k1.privKeyNegate(sec)
assertEquals(
Hex.encode(sec1).toUpperCase(),
"981A9A7DD677A622518DA068D66D5F824E5F22F084B8A0E2F195B5662F300C11",
"testPrivKeyNegate"
)
val sec2: ByteArray = Secp256k1.privKeyNegate(sec1)
assertTrue(sec.contentEquals(sec2))
}
/**
* This tests private key tweak-add
*/
@Test
fun testPrivKeyTweakAdd_1() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.privKeyTweakAdd(sec, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(sigString, "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3", "testPrivKeyAdd_1")
}
/**
* This tests private key tweak-mul
*/
@Test
fun testPrivKeyTweakMul_1() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.privKeyTweakMul(sec, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(sigString, "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC", "testPrivKeyMul_1")
}
/**
* This tests private key tweak-add uncompressed
*/
@Test
fun testPrivKeyTweakAdd_2() {
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.pubKeyTweakAdd(pub, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF",
"testPrivKeyAdd_2"
)
}
/**
* This tests private key tweak-mul uncompressed
*/
@Test
fun testPrivKeyTweakMul_2() {
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()) //sha256hash of "tweak"
val resultArr: ByteArray = Secp256k1.pubKeyTweakMul(pub, data)
val sigString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
sigString,
"04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589",
"testPrivKeyMul_2"
)
}
/**
* This tests seed randomization
*/
@Test
fun testRandomize() {
val seed: ByteArray = Hex.decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()) //sha256hash of "random"
val result: Boolean = Secp256k1.randomize(seed)
assertEquals(result, true, "testRandomize")
}
@Test
fun testCreateECDHSecret() {
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase())
val resultArr: ByteArray = Secp256k1.createECDHSecret(sec, pub)
val ecdhString: String = Hex.encode(resultArr).toUpperCase()
assertEquals(
ecdhString,
"2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043",
"testCreateECDHSecret"
)
}
@Test
fun testEcdsaRecover() {
val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()) //sha256hash of "testing"
val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase())
val pub: ByteArray = Secp256k1.computePubkey(sec)
val sig: ByteArray = Secp256k1.signCompact(data, sec)
val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0)
val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1)
assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1), "testEcdsaRecover")
}
}

View File

@@ -0,0 +1,47 @@
package fr.acinq.secp256k1
import kotlin.jvm.JvmStatic
object Hex {
private val hexCode = arrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
@JvmStatic
fun decode(hex: String): ByteArray {
val input = hex.filterNot { it.isWhitespace() }
val offset = when {
input.length >= 2 && input[0] == '0' && input[1] == 'x' -> 2
input.length >= 2 && input[0] == '0' && input[1] == 'X' -> 2
else -> 0
}
val len = input.length - offset
require(len % 2 == 0)
val out = ByteArray(len / 2)
fun hexToBin(ch: Char): Int = when (ch) {
in '0'..'9' -> ch - '0'
in 'a'..'f' -> ch - 'a' + 10
in 'A'..'F' -> ch - 'A' + 10
else -> throw IllegalArgumentException("illegal hex character: $ch")
}
for (i in out.indices) {
out[i] = (hexToBin(input[offset + 2 * i]) * 16 + hexToBin(input[offset + 2 * i + 1])).toByte()
}
return out
}
@JvmStatic
fun encode(input: ByteArray, offset: Int, len: Int): String {
val r = StringBuilder(len * 2)
for (i in 0 until len) {
val b = input[offset + i]
r.append(hexCode[(b.toInt() shr 4) and 0xF])
r.append(hexCode[b.toInt() and 0xF])
}
return r.toString()
}
@JvmStatic
fun encode(input: ByteArray): String = encode(input, 0, input.size)
}

View File

@@ -0,0 +1,228 @@
package fr.acinq.secp256k1
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.*
/*--------------------------------------------------------------------------
* Copyright 2008 Taro L. Saito
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*--------------------------------------------------------------------------*/ /**
* Provides OS name and architecture name.
*
* @author leo
*/
@Suppress("DuplicatedCode")
internal object OSInfo {
private val archMapping = HashMap<String, String>()
private const val X86 = "x86"
private const val X86_64 = "x86_64"
private const val IA64_32 = "ia64_32"
private const val IA64 = "ia64"
private const val PPC = "ppc"
private const val PPC64 = "ppc64"
@JvmStatic val nativeSuffix: String get() = "$os-$arch"
@JvmStatic val os: String get() = translateOSName(System.getProperty("os.name"))
@JvmStatic val hardwareName: String get() =
try {
val p = Runtime.getRuntime().exec("uname -m")
p.waitFor()
val input = p.inputStream
input.use {
val b = ByteArrayOutputStream()
val buf = ByteArray(32)
var readLen = it.read(buf, 0, buf.size)
while (readLen >= 0) {
b.write(buf, 0, readLen)
readLen = it.read(buf, 0, buf.size)
}
b.toString()
}
} catch (e: Throwable) {
System.err.println("Error while running uname -m: " + e.message)
"unknown"
}
@JvmStatic
private fun resolveArmArchType(): String {
if (System.getProperty("os.name").contains("Linux")) {
val armType = hardwareName
// armType (uname -m) can be armv5t, armv5te, armv5tej, armv5tejl, armv6, armv7, armv7l, aarch64, i686// ignored: fall back to "arm" arch (soft-float ABI)
// ignored: fall back to "arm" arch (soft-float ABI)
// determine if first JVM found uses ARM hard-float ABI
when {
armType.startsWith("armv6") -> {
// Raspberry PI
return "armv6"
}
armType.startsWith("armv7") -> {
// Generic
return "armv7"
}
armType.startsWith("armv5") -> {
// Use armv5, soft-float ABI
return "arm"
}
armType == "aarch64" -> {
// Use arm64
return "arm64"
}
// Java 1.8 introduces a system property to determine armel or armhf
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545
// For java7, we stil need to if run some shell commands to determine ABI of JVM
else -> {
val abi = System.getProperty("sun.arch.abi")
if (abi != null && abi.startsWith("gnueabihf")) {
return "armv7"
}
// For java7, we stil need to if run some shell commands to determine ABI of JVM
val javaHome = System.getProperty("java.home")
try {
// determine if first JVM found uses ARM hard-float ABI
var exitCode = Runtime.getRuntime().exec("which readelf").waitFor()
if (exitCode == 0) {
val cmdarray = arrayOf(
"/bin/sh", "-c", "find '" + javaHome +
"' -name 'libjvm.so' | head -1 | xargs readelf -A | " +
"grep 'Tag_ABI_VFP_args: VFP registers'"
)
exitCode = Runtime.getRuntime().exec(cmdarray).waitFor()
if (exitCode == 0) {
return "armv7"
}
} else {
System.err.println(
"WARNING! readelf not found. Cannot check if running on an armhf system, " +
"armel architecture will be presumed."
)
}
} catch (e: IOException) {
// ignored: fall back to "arm" arch (soft-float ABI)
} catch (e: InterruptedException) {
// ignored: fall back to "arm" arch (soft-float ABI)
}
}
}
// Java 1.8 introduces a system property to determine armel or armhf
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8005545
val abi = System.getProperty("sun.arch.abi")
if (abi != null && abi.startsWith("gnueabihf")) {
return "armv7"
}
// For java7, we stil need to if run some shell commands to determine ABI of JVM
val javaHome = System.getProperty("java.home")
try {
// determine if first JVM found uses ARM hard-float ABI
var exitCode = Runtime.getRuntime().exec("which readelf").waitFor()
if (exitCode == 0) {
val cmdarray = arrayOf(
"/bin/sh", "-c", "find '" + javaHome +
"' -name 'libjvm.so' | head -1 | xargs readelf -A | " +
"grep 'Tag_ABI_VFP_args: VFP registers'"
)
exitCode = Runtime.getRuntime().exec(cmdarray).waitFor()
if (exitCode == 0) {
return "armv7"
}
} else {
System.err.println(
"WARNING! readelf not found. Cannot check if running on an armhf system, " +
"armel architecture will be presumed."
)
}
} catch (e: IOException) {
// ignored: fall back to "arm" arch (soft-float ABI)
} catch (e: InterruptedException) {
// ignored: fall back to "arm" arch (soft-float ABI)
}
}
// Use armv5, soft-float ABI
return "arm"
}
// For Android
@JvmStatic
val arch: String?
get() {
val systemOsArch = System.getProperty("os.arch")
val osArch =
if (systemOsArch.startsWith("arm")) {
resolveArmArchType()
} else {
val lc = systemOsArch.toLowerCase(Locale.US)
if (archMapping.containsKey(lc)) return archMapping[lc]
systemOsArch
}
return translateArchNameToFolderName(osArch)
}
@JvmStatic
fun translateOSName(osName: String): String =
when {
osName.contains("Windows") -> "mingw"
osName.contains("Mac") || osName.contains("Darwin") -> "darwin"
osName.contains("Linux") -> "linux"
osName.contains("AIX") -> "aix"
else -> osName.replace("\\W".toRegex(), "")
}
@JvmStatic
fun translateArchNameToFolderName(archName: String): String = archName.replace("\\W".toRegex(), "")
init {
// x86 mappings
archMapping[X86] = X86
archMapping["i386"] = X86
archMapping["i486"] = X86
archMapping["i586"] = X86
archMapping["i686"] = X86
archMapping["pentium"] = X86
// x86_64 mappings
archMapping[X86_64] = X86_64
archMapping["amd64"] = X86_64
archMapping["em64t"] = X86_64
archMapping["universal"] = X86_64 // Needed for openjdk7 in Mac
// Itenium 64-bit mappings
archMapping[IA64] = IA64
archMapping["ia64w"] = IA64
// Itenium 32-bit mappings, usually an HP-UX construct
archMapping[IA64_32] = IA64_32
archMapping["ia64n"] = IA64_32
// PowerPC mappings
archMapping[PPC] = PPC
archMapping["power"] = PPC
archMapping["powerpc"] = PPC
archMapping["power_pc"] = PPC
archMapping["power_rs"] = PPC
// TODO: PowerPC 64bit mappings
archMapping[PPC64] = PPC64
archMapping["power64"] = PPC64
archMapping["powerpc64"] = PPC64
archMapping["power_pc64"] = PPC64
archMapping["power_rs64"] = PPC64
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2020 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.secp256k1
import org.bitcoin.NativeSecp256k1
import java.math.BigInteger
public actual object Secp256k1 {
init {
Secp256k1Loader.initialize()
}
public actual fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean = NativeSecp256k1.verify(data, signature, pub)
public actual fun sign(data: ByteArray, sec: ByteArray): ByteArray = NativeSecp256k1.sign(data, sec)
public actual fun signCompact(data: ByteArray, sec: ByteArray): ByteArray = NativeSecp256k1.signCompact(data, sec)
public actual fun secKeyVerify(seckey: ByteArray): Boolean = NativeSecp256k1.secKeyVerify(seckey)
public actual fun computePubkey(seckey: ByteArray): ByteArray = NativeSecp256k1.computePubkey(seckey)
public actual fun parsePubkey(pubkey: ByteArray): ByteArray = NativeSecp256k1.parsePubkey(pubkey)
public actual fun cleanup(): Unit = NativeSecp256k1.cleanup()
public actual fun cloneContext(): Long = NativeSecp256k1.cloneContext()
public actual fun privKeyNegate(privkey: ByteArray): ByteArray = NativeSecp256k1.privKeyNegate(privkey)
public actual fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray = NativeSecp256k1.privKeyTweakMul(privkey, tweak)
public actual fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray = NativeSecp256k1.privKeyTweakAdd(privkey, tweak)
public actual fun pubKeyNegate(pubkey: ByteArray): ByteArray = NativeSecp256k1.pubKeyNegate(pubkey)
public actual fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray = NativeSecp256k1.pubKeyTweakAdd(pubkey, tweak)
public actual fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray = NativeSecp256k1.pubKeyTweakMul(pubkey, tweak)
public actual fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray = NativeSecp256k1.pubKeyAdd(pubkey1, pubkey2)
public actual fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray = NativeSecp256k1.createECDHSecret(seckey, pubkey)
public actual fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray = NativeSecp256k1.ecdsaRecover(sig, message, recid)
public actual fun randomize(seed: ByteArray): Boolean = NativeSecp256k1.randomize(seed)
}

View File

@@ -0,0 +1,219 @@
package fr.acinq.secp256k1
import java.io.*
import java.util.*
/*--------------------------------------------------------------------------
* Copyright 2007 Taro L. Saito
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*--------------------------------------------------------------------------*/ /**
* Set the system properties, org.sqlite.lib.path, org.sqlite.lib.name,
* appropriately so that the SQLite JDBC driver can find *.dll, *.jnilib and
* *.so files, according to the current OS (win, linux, mac).
* The library files are automatically extracted from this project's package (JAR).
* usage: call [.initialize] before using SQLite JDBC driver.
*
* @author leo
*/
public object Secp256k1Loader {
private var extracted = false
/**
* Loads secp256k1 native library.
*
* @return True if secp256k1 native library is successfully loaded; false otherwise.
* @throws Exception if loading fails
*/
@JvmStatic
@Synchronized
@Throws(Exception::class)
public fun initialize(): Boolean {
// only cleanup before the first extract
if (!extracted) {
cleanup()
}
loadSecp256k1NativeLibrary()
return extracted
}
private val tempDir: File
get() = File(System.getProperty("fr.acinq.secp256k1.tmpdir", System.getProperty("java.io.tmpdir")))
/**
* Deleted old native libraries e.g. on Windows the DLL file is not removed
* on VM-Exit (bug #80)
*/
@JvmStatic
public fun cleanup() {
val tempFolder = tempDir.absolutePath
val dir = File(tempFolder)
val nativeLibFiles = dir.listFiles(object : FilenameFilter {
private val searchPattern = "secp256k1-"
override fun accept(dir: File, name: String): Boolean {
return name.startsWith(searchPattern) && !name.endsWith(".lck")
}
})
if (nativeLibFiles != null) {
for (nativeLibFile in nativeLibFiles) {
val lckFile = File(nativeLibFile.absolutePath + ".lck")
if (!lckFile.exists()) {
try {
nativeLibFile.delete()
} catch (e: SecurityException) {
System.err.println("Failed to delete old native lib" + e.message)
}
}
}
}
}
@Throws(IOException::class)
private fun InputStream.contentsEquals(other: InputStream): Boolean {
val bufThis = this as? BufferedInputStream ?: BufferedInputStream(this)
val bufOther = other as? BufferedInputStream ?: BufferedInputStream(other)
var ch = bufThis.read()
while (ch != -1) {
val ch2 = bufOther.read()
if (ch != ch2) return false
ch = bufThis.read()
}
val ch2 = bufOther.read()
return ch2 == -1
}
/**
* Extracts and loads the specified library file to the target folder
*
* @param libDir Library path.
* @param libFileName Library name.
* @param targetDirectory Target folder.
* @return
*/
private fun extractAndLoadLibraryFile(libDir: String, libFileName: String, targetDirectory: String): Boolean {
val libPath = "$libDir/$libFileName"
// Include architecture name in temporary filename in order to avoid conflicts
// when multiple JVMs with different architectures running at the same time
val uuid = UUID.randomUUID().toString()
val extractedLibFileName = String.format("secp256k1-%s-%s", uuid, libFileName)
val extractedLckFileName = "$extractedLibFileName.lck"
val extractedLibFile = File(targetDirectory, extractedLibFileName)
val extractedLckFile = File(targetDirectory, extractedLckFileName)
return try {
// Extract a native library file into the target directory
val reader = Secp256k1Loader::class.java.getResourceAsStream(libPath)
if (!extractedLckFile.exists()) {
FileOutputStream(extractedLckFile).close()
}
val writer = FileOutputStream(extractedLibFile)
try {
val buffer = ByteArray(8192)
var bytesRead = reader.read(buffer)
while (bytesRead != -1) {
writer.write(buffer, 0, bytesRead)
bytesRead = reader.read(buffer)
}
} finally {
// Delete the extracted lib file on JVM exit.
extractedLibFile.deleteOnExit()
extractedLckFile.deleteOnExit()
writer.close()
reader.close()
}
// Set executable (x) flag to enable Java to load the native library
extractedLibFile.setReadable(true)
extractedLibFile.setWritable(true, true)
extractedLibFile.setExecutable(true)
// Check whether the contents are properly copied from the resource folder
Secp256k1Loader::class.java.getResourceAsStream(libPath).use { nativeIn ->
FileInputStream(extractedLibFile).use { extractedLibIn ->
if (!nativeIn.contentsEquals(extractedLibIn)) {
throw RuntimeException(
String.format(
"Failed to write a native library file at %s",
extractedLibFile
)
)
}
}
}
loadNativeLibrary(targetDirectory, extractedLibFileName)
} catch (e: IOException) {
System.err.println(e.message)
false
}
}
/**
* Loads native library using the given path and name of the library.
*
* @param path Path of the native library.
* @param name Name of the native library.
* @return True for successfully loading; false otherwise.
*/
private fun loadNativeLibrary(path: String, name: String): Boolean {
val libPath = File(path, name)
return if (libPath.exists()) {
try {
System.load(File(path, name).absolutePath)
true
} catch (e: UnsatisfiedLinkError) {
System.err.println("Failed to load native library:$name. osinfo: ${OSInfo.nativeSuffix}")
System.err.println(e)
false
}
} else {
false
}
}
/**
* Loads secp256k1 native library using given path and name of the library.
*
* @throws
*/
private fun loadSecp256k1NativeLibrary() {
if (extracted) {
return
}
// Try loading library from fr.acinq.secp256k1.lib.path library path */
val libraryPath = System.getProperty("fr.acinq.secp256k1.lib.path")
val libraryName = System.getProperty("fr.acinq.secp256k1.lib.name") ?: System.mapLibraryName("secp256k1-jni-${OSInfo.nativeSuffix}")
if (libraryPath != null) {
if (loadNativeLibrary(libraryPath, libraryName)) {
extracted = true
return
}
}
// Load the os-dependent library from the jar file
val packagePath = Secp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/")
val embeddedLibraryPath = "/$packagePath/native"
val hasNativeLib = Secp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null
if (!hasNativeLib) {
error("No native library found: at $embeddedLibraryPath/$libraryName")
}
// Try extracting the library from jar
if (extractAndLoadLibraryFile(embeddedLibraryPath, libraryName, tempDir.absolutePath)) {
extracted = true
return
}
extracted = false
return
}
}

View File

@@ -0,0 +1,508 @@
/*
* Copyright 2013 Google Inc.
* Copyright 2014-2016 the libsecp256k1 contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoin
import org.bitcoin.NativeSecp256k1Util.AssertFailException
import java.math.BigInteger
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantReadWriteLock
/**
*
* This class holds native methods to handle ECDSA verification.
*
*
* You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1
*
*
* To build secp256k1 for use with bitcoinj, run
* `./configure --enable-jni --enable-experimental --enable-module-ecdh`
* and `make` then copy `.libs/libsecp256k1.so` to your system library path
* or point the JVM to the folder containing it with -Djava.library.path
*
*/
public object NativeSecp256k1 {
private val rwl = ReentrantReadWriteLock()
private val r: Lock = rwl.readLock()
private val w: Lock = rwl.writeLock()
private val nativeECDSABuffer = ThreadLocal<ByteBuffer?>()
private fun pack(vararg buffers: ByteArray): ByteBuffer {
var size = 0
for (i in buffers.indices) {
size += buffers[i].size
}
val byteBuff = nativeECDSABuffer.get()?.takeIf { it.capacity() >= size }
?: ByteBuffer.allocateDirect(size).also {
it.order(ByteOrder.nativeOrder())
nativeECDSABuffer.set(it)
}
byteBuff.rewind()
for (i in buffers.indices) {
byteBuff.put(buffers[i])
}
return byteBuff
}
/**
* Verifies the given secp256k1 signature in native code.
* Calling when enabled == false is undefined (probably library not loaded)
*
* @param data The data which was signed, must be exactly 32 bytes
* @param signature The signature
* @param pub The public key which did the signing
* @return true if the signature is valid
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun verify(data: ByteArray, signature: ByteArray, pub: ByteArray): Boolean {
require(data.size == 32 && signature.size <= 520 && pub.size <= 520)
val byteBuff = pack(data, signature, pub)
r.lock()
return try {
secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.size, pub.size) == 1
} finally {
r.unlock()
}
}
/**
* libsecp256k1 Create an ECDSA signature.
*
* @param data Message hash, 32 bytes
* @param sec Secret key, 32 bytes
* @return a signature, or an empty array is signing failed
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun sign(data: ByteArray, sec: ByteArray): ByteArray {
require(data.size == 32 && sec.size <= 32)
val byteBuff = pack(data, sec)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val sigArr = retByteArray[0]
val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(sigArr.size, sigLen, "Got bad signature length.")
return if (retVal == 0) ByteArray(0) else sigArr
}
/**
* libsecp256k1 Create an ECDSA signature.
*
* @param data Message hash, 32 bytes
* @param sec Secret key, 32 bytes
*
*
* Return values
* @return a signature, or an empty array is signing failed
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun signCompact(data: ByteArray, sec: ByteArray): ByteArray {
require(data.size == 32 && sec.size <= 32)
val byteBuff = pack(data, sec)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ecdsa_sign_compact(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val sigArr = retByteArray[0]
val sigLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(sigArr.size, sigLen, "Got bad signature length.")
return if (retVal == 0) ByteArray(0) else sigArr
}
/**
* libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
*
* @param seckey ECDSA Secret key, 32 bytes
* @return true if seckey is valid
*/
@JvmStatic
public fun secKeyVerify(seckey: ByteArray): Boolean {
require(seckey.size == 32)
val byteBuff = pack(seckey)
r.lock()
return try {
secp256k1_ec_seckey_verify(byteBuff, Secp256k1Context.getContext()) == 1
} finally {
r.unlock()
}
}
/**
* libsecp256k1 Compute Pubkey - computes public key from secret key
*
* @param seckey ECDSA Secret key, 32 bytes
* @throws AssertFailException if parameters are not valid
* @return the corresponding public key (uncompressed)
*/
//TODO add a 'compressed' arg
@JvmStatic
@Throws(AssertFailException::class)
public fun computePubkey(seckey: ByteArray): ByteArray {
require(seckey.size == 32)
val byteBuff = pack(seckey)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.")
return if (retVal == 0) ByteArray(0) else pubArr
}
/**
* @param pubkey public key
* @return the input public key (uncompressed) if valid, or an empty array
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun parsePubkey(pubkey: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
val byteBuff = pack(pubkey)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ec_pubkey_parse(byteBuff, Secp256k1Context.getContext(), pubkey.size)
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, 65, "Got bad pubkey length.")
return if (retVal == 0) ByteArray(0) else pubArr
}
/**
* libsecp256k1 Cleanup - This destroys the secp256k1 context object
* This should be called at the end of the program for proper cleanup of the context.
*/
@JvmStatic
@Synchronized
public fun cleanup() {
w.lock()
try {
secp256k1_destroy_context(Secp256k1Context.getContext())
} finally {
w.unlock()
}
}
@JvmStatic
public fun cloneContext(): Long {
r.lock()
return try {
secp256k1_ctx_clone(Secp256k1Context.getContext())
} finally {
r.unlock()
}
}
@JvmStatic
@Throws(AssertFailException::class)
public fun privKeyNegate(privkey: ByteArray): ByteArray {
require(privkey.size == 32)
val byteBuff = pack(privkey)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_privkey_negate(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val privArr = retByteArray[0]
val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(privArr.size, privLen, "Got bad privkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return privArr
}
/**
* libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
*
* @param privkey 32-byte seckey
* @param tweak some bytes to tweak with
* @return privkey * tweak
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32)
val byteBuff = pack(privkey, tweak!!)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_privkey_tweak_mul(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val privArr = retByteArray[0]
val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(privArr.size, privLen, "Got bad privkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return privArr
}
/**
* libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
*
* @param privkey 32-byte seckey
* @param tweak some bytes to tweak with
* @return privkey + tweak
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray {
require(privkey.size == 32)
val byteBuff = pack(privkey, tweak!!)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_privkey_tweak_add(byteBuff, Secp256k1Context.getContext())
} finally {
r.unlock()
}
val privArr = retByteArray[0]
val privLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(privArr.size, privLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return privArr
}
@JvmStatic
@Throws(AssertFailException::class)
public fun pubKeyNegate(pubkey: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
val byteBuff = pack(pubkey)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_pubkey_negate(byteBuff, Secp256k1Context.getContext(), pubkey.size)
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return pubArr
}
/**
* libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
*
* @param tweak some bytes to tweak with
* @param pubkey 32-byte seckey
* @return pubkey + tweak
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
val byteBuff = pack(pubkey, tweak!!)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_pubkey_tweak_add(byteBuff, Secp256k1Context.getContext(), pubkey.size)
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return pubArr
}
/**
* libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
*
* @param tweak some bytes to tweak with
* @param pubkey 32-byte seckey
* @return pubkey * tweak
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
require(pubkey.size == 33 || pubkey.size == 65)
val byteBuff = pack(pubkey, tweak!!)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_pubkey_tweak_mul(byteBuff, Secp256k1Context.getContext(), pubkey.size)
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(pubArr.size, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return pubArr
}
@JvmStatic
@Throws(AssertFailException::class)
public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray {
require(pubkey1.size == 33 || pubkey1.size == 65)
require(pubkey2.size == 33 || pubkey2.size == 65)
val byteBuff = pack(pubkey1, pubkey2)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ec_pubkey_add(byteBuff, Secp256k1Context.getContext(), pubkey1.size, pubkey2.size)
} finally {
r.unlock()
}
val pubArr = retByteArray[0]
val pubLen: Int = BigInteger(byteArrayOf(retByteArray[1][0])).toInt() and 0xFF
val retVal = BigInteger(byteArrayOf(retByteArray[1][1])).toInt()
NativeSecp256k1Util.assertEquals(65, pubLen, "Got bad pubkey length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return pubArr
}
/**
* libsecp256k1 create ECDH secret - constant time ECDH calculation
*
* @param seckey byte array of secret key used in exponentiaion
* @param pubkey byte array of public key used in exponentiaion
* @return ecdh(sedckey, pubkey)
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Throws(AssertFailException::class)
public fun createECDHSecret(seckey: ByteArray, pubkey: ByteArray): ByteArray {
require(seckey.size <= 32 && pubkey.size <= 65)
val byteBuff = pack(seckey, pubkey)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.size)
} finally {
r.unlock()
}
val resArr = retByteArray[0]
val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
NativeSecp256k1Util.assertEquals(resArr.size, 32, "Got bad result length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return resArr
}
@JvmStatic
@Throws(AssertFailException::class)
public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
require(sig.size == 64)
require(message.size == 32)
val byteBuff = pack(sig, message)
val retByteArray: Array<ByteArray>
r.lock()
retByteArray = try {
secp256k1_ecdsa_recover(byteBuff, Secp256k1Context.getContext(), recid)
} finally {
r.unlock()
}
val resArr = retByteArray[0]
val retVal = BigInteger(byteArrayOf(retByteArray[1][0])).toInt()
NativeSecp256k1Util.assertEquals(resArr.size, 65, "Got bad result length.")
NativeSecp256k1Util.assertEquals(retVal, 1, "Failed return value check.")
return resArr
}
/**
* libsecp256k1 randomize - updates the context randomization
*
* @param seed 32-byte random seed
* @return true if successful
* @throws AssertFailException in case of failure
*/
@JvmStatic
@Synchronized
@Throws(AssertFailException::class)
public fun randomize(seed: ByteArray): Boolean {
require(seed.size == 32)
val byteBuff = pack(seed)
w.lock()
return try {
secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1
} finally {
w.unlock()
}
}
@JvmStatic private external fun secp256k1_ctx_clone(context: Long): Long
@JvmStatic private external fun secp256k1_context_randomize(byteBuff: ByteBuffer, context: Long): Int
@JvmStatic private external fun secp256k1_privkey_negate(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_privkey_tweak_add(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_privkey_tweak_mul(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_pubkey_negate(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_pubkey_tweak_add(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_pubkey_tweak_mul(byteBuff: ByteBuffer, context: Long, pubLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_destroy_context(context: Long)
@JvmStatic private external fun secp256k1_ecdsa_verify(byteBuff: ByteBuffer, context: Long, sigLen: Int, pubLen: Int): Int
@JvmStatic private external fun secp256k1_ecdsa_sign(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_sign_compact(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_seckey_verify(byteBuff: ByteBuffer, context: Long): Int
@JvmStatic private external fun secp256k1_ec_pubkey_create(byteBuff: ByteBuffer, context: Long): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_parse(
byteBuff: ByteBuffer,
context: Long,
inputLen: Int
): Array<ByteArray>
@JvmStatic private external fun secp256k1_ec_pubkey_add(
byteBuff: ByteBuffer,
context: Long,
lent1: Int,
len2: Int
): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdh(byteBuff: ByteBuffer, context: Long, inputLen: Int): Array<ByteArray>
@JvmStatic private external fun secp256k1_ecdsa_recover(byteBuff: ByteBuffer, context: Long, recid: Int): Array<ByteArray>
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014-2016 the libsecp256k1 contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoin
import kotlin.jvm.Throws
import java.lang.Exception
internal object NativeSecp256k1Util {
@Throws(AssertFailException::class)
fun assertEquals(val1: Int, val2: Int, message: String) {
if (val1 != val2) throw AssertFailException("FAIL: $message")
}
class AssertFailException(message: String?) : Exception(message)
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2014-2016 the libsecp256k1 contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoin
import fr.acinq.secp256k1.Secp256k1Loader.initialize
/**
* This class holds the context reference used in native methods
* to handle ECDSA operations.
*/
public object Secp256k1Context {
@JvmStatic
public val isEnabled: Boolean //true if the library is loaded
private val context: Long //ref to pointer to context obj
@JvmStatic
public fun getContext(): Long {
return if (!isEnabled) -1 else context //sanity check
}
@JvmStatic private external fun secp256k1_init_context(): Long
init { //static initializer
var isEnabled = true
var contextRef: Long = -1
try {
if ("The Android Project" == System.getProperty("java.vm.vendor")) {
System.loadLibrary("secp256k1")
} else {
initialize()
}
contextRef = secp256k1_init_context()
} catch (e: UnsatisfiedLinkError) {
println("Cannot load secp256k1 native library: $e")
isEnabled = false
} catch (e: Exception) {
println("Cannot load secp256k1 native library: $e")
isEnabled = false
}
this.isEnabled = isEnabled
this.context = contextRef
}
}