Skip to content

Commit

Permalink
fix: Allow Apollo to verify der signatures from bc and bitcoin seamle…
Browse files Browse the repository at this point in the history
…ssly. Will try validating bouncyCastle or convert to bouncy castle from bitcoin format.
  • Loading branch information
elribonazo committed Apr 11, 2024
1 parent 4b9b207 commit a73f59f
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import kotlin.test.assertEquals
import kotlin.test.assertTrue

class Secp256k1LibTests {


@Test
fun testCreatePublicKey() {
val privKeyBase64 = "N/JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg="
Expand Down Expand Up @@ -84,4 +86,5 @@ class Secp256k1LibTests {
"BHza5mV6_Iz6XdyMpxpjUMprZUCN_MpMuQCTFYpxSf8rW7N7DD04troywCgLkg0_ABP-IcxZcE1-qKjwCWYTVO8"
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ actual class Secp256k1Lib actual constructor() {
): Boolean {
val ecjs = ec("secp256k1")
val sha = SHA256().digest(data)

return ecjs.verify(sha.toHexString(), signature.toHexString(), publicKey.toHexString(), enc = "hex")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package io.iohk.atala.prism.apollo.secp256k1

import fr.acinq.secp256k1.Secp256k1
import org.bouncycastle.asn1.*
import org.bouncycastle.jce.ECNamedCurveTable
import org.kotlincrypto.hash.sha2.SHA256
import java.io.ByteArrayOutputStream
import java.math.BigInteger


/**
* This class provides various Secp256k1 cryptographic functionalities such as creating public keys, signing data,
Expand Down Expand Up @@ -60,10 +65,61 @@ actual class Secp256k1Lib {
signature: ByteArray,
data: ByteArray
): Boolean {
val sha = SHA256().digest(data)
return Secp256k1.verify(signature, sha, publicKey)
val sha = SHA256().digest(data);

if ( Secp256k1.verify(signature, sha, publicKey)) {
return true
}

val normalisedSignature = Secp256k1.signatureNormalize(signature).first
val derSignature = transcodeSignatureToDERBitcoin(normalisedSignature);

if ( Secp256k1.verify(derSignature, sha, publicKey)) {
return true;
}

return false
}

private fun reverseB32(inputBytes: ByteArray): ByteArray {
val reversedBytes = ByteArray(inputBytes.size)
for (i in inputBytes.indices) {
reversedBytes[inputBytes.size - i - 1] = inputBytes[i]
}
return reversedBytes
}

private fun transcodeSignatureToDERBitcoin(signature: ByteArray): ByteArray {

val rawLen = signature.size / 2

val bigR = BigInteger(1, signature.copyOfRange(0, rawLen))
val bigS = BigInteger(1, signature.copyOfRange(rawLen, signature.size))
var r = bigR.toByteArray()
var s = bigS.toByteArray()

r = reverseB32(r)
s = reverseB32(s)

val lenR = r.size
val lenS = s.size

val derLength = 6 + lenR + lenS
val derSignature = ByteArray(derLength)

derSignature[0] = 0x30
derSignature[1] = (4 + lenR + lenS).toByte()
derSignature[2] = 0x02
derSignature[3] = lenR.toByte()
System.arraycopy(r, 0, derSignature, 4, lenR)
derSignature[4 + lenR] = 0x02
derSignature[5 + lenR] = lenS.toByte()
System.arraycopy(s, 0, derSignature, 6 + lenR, lenS)

return derSignature
}


/**
* Decompresses a compressed public key.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.iohk.atala.prism.apollo.utils

import io.iohk.atala.prism.apollo.base64.base64UrlDecodedBytes
import io.iohk.atala.prism.apollo.secp256k1.Secp256k1Lib
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class Secp256k1LibJVMTests {


@Test
fun testVerification() {
val pubKeyBase64 = "BD-l4lrQ6Go-oN5XtdpY6o5dyf2V2v5EbMAvRjVGJpE1gYVURJfxKMpNPnKlLr4MOLNVaYvBNOoy9L50E8jVx8Q"
val signatureBase64 =
"MEUCIQCFeGlhJrH-9R70X4JzrurWs52SwuxCnJ8ky6riFwMOrwIgT7zlLo7URMHW5tiMgG73IOw2Dm3XyLl1iqW1-t5NFWQ"
val message = "Test"

assertTrue {
Secp256k1Lib().verify(
pubKeyBase64.base64UrlDecodedBytes,
signatureBase64.base64UrlDecodedBytes,
message.encodeToByteArray()
)
}
}

@OptIn(ExperimentalStdlibApi::class)
@Test
fun testSignatureNormalisationFromJS() {
val signatureHex = "3045022100bca0478b0f400277626355d9f411bf0534afb1236758099c0c8ffdd761664a7202207e629a601f1bbe3e0df10899e62919c2a5c902f773120a2bddeb1cde14bae1fd"
val publicKeyHex = "042e6d481b0b8761414166ea01431ed339d9150a58c2e521cb6cc8638988299b835acfc2cc1c7a7fd9ea54acc8060c37367be8ed7fb70c13203e702ef7a20d8b06"
val dataHex = "4174616c61507269736d2057616c6c65742053444b";
val publicKey = publicKeyHex.hexToByteArray()
val signature = signatureHex.hexToByteArray();
val data = dataHex.hexToByteArray();
val valid = Secp256k1Lib().verify(publicKey,signature, data)
assertEquals( valid, true)
}

@Test
fun testJVMVerification() {
val pubKeyBase64 = "BD-l4lrQ6Go-oN5XtdpY6o5dyf2V2v5EbMAvRjVGJpE1gYVURJfxKMpNPnKlLr4MOLNVaYvBNOoy9L50E8jVx8Q"
val signatureBase64 = "MEUCIQCFeGlhJrH-9R70X4JzrurWs52SwuxCnJ8ky6riFwMOrwIgT7zlLo7URMHW5tiMgG73IOw2Dm3XyLl1iqW1-t5NFWQ"
val message = "Test"
assertTrue {
Secp256k1Lib().verify(
pubKeyBase64.base64UrlDecodedBytes,
signatureBase64.base64UrlDecodedBytes,
message.encodeToByteArray()
)
}
}


@Test
fun testJVMVerificationBitcoin() {
val ecPublicKeyBase64 = "BC7OYUnD57Qxel3-gyGuUIeicvRYhkFMOw9vsz70WMHzt8X8jiX358Jv9KYrMOHpkHE6jpb8CTvGabgIJUPkX_4"
val messageBase64 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJub25jZSI6IjQ3YmM5ZmMwLWVhODAtNDlmOC04OTcxLWJjYzY0MmJmZDNjMCIsImlzcyI6ImRpZDpwcmlzbTphZjJlNGJiOWU1MTRmODg5ZTdkNTY2MDZjNmYzZWVhYmNmMDgxZTc0ZTQ4NDMwN2Q3NTQ4Mzg0Y2ZiOTE4ZTdlOkNzY0JDc1FCRW1RS0QyRjFkR2hsYm5ScFkyRjBhVzl1TUJBRVFrOEtDWE5sWTNBeU5UWnJNUklnTHM1aFNjUG50REY2WGY2RElhNVFoNkp5OUZpR1FVdzdEMi16UHZSWXdmTWFJTGZGX0k0bDktZkNiX1NtS3pEaDZaQnhPbzZXX0FrN3htbTRDQ1ZENUZfLUVsd0tCMjFoYzNSbGNqQVFBVUpQQ2dselpXTndNalUyYXpFU0lDN09ZVW5ENTdReGVsMy1neUd1VUllaWN2Ulloa0ZNT3c5dnN6NzBXTUh6R2lDM3hmeU9KZmZud21fMHBpc3c0ZW1RY1RxT2x2d0pPOFpwdUFnbFEtUmZfZyIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOlwvXC93d3cudzMub3JnXC8yMDE4XC9wcmVzZW50YXRpb25zXC92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXX0sImF1ZCI6ImRvbWFpbiJ9"
val rawSignatureBase64 = "ZGjNy5vyOaDCPfZRqpjbolcPZXD3WmM_VugjIgVhY2ANARaJ_PnNCnTFFYUgajzml8kIhyIPQsVOchQDQz1RMA"
val swiftSignatureBase64 = "MEQCIGBjYQUiI-hWP2Na93BlD1ei25iqUfY9wqA58pvLzWhkAiAwUT1DAxRyTsVCDyKHCMmX5jxqIIUVxXQKzfn8iRYBDQ"
val nimbusDerSignatureBase64 = "MEQCIGRozcub8jmgwj32UaqY26JXD2Vw91pjP1boIyIFYWNgAiANARaJ_PnNCnTFFYUgajzml8kIhyIPQsVOchQDQz1RMA=="

val publicKey = ecPublicKeyBase64.base64UrlDecodedBytes
val message = messageBase64.encodeToByteArray()

assertTrue {
Secp256k1Lib().verify(
publicKey,
rawSignatureBase64.base64UrlDecodedBytes,
message
)
}

assertTrue {
Secp256k1Lib().verify(
publicKey,
swiftSignatureBase64.base64UrlDecodedBytes,
message
)
}

assertTrue {
Secp256k1Lib().verify(
publicKey,
nimbusDerSignatureBase64.base64UrlDecodedBytes,
message
)
}
}

}

0 comments on commit a73f59f

Please sign in to comment.