diff --git a/tangem-core/build.gradle b/tangem-core/build.gradle index c6a765cb11..e3cb3b5cba 100644 --- a/tangem-core/build.gradle +++ b/tangem-core/build.gradle @@ -1,4 +1,5 @@ apply plugin: "kotlin" +apply plugin: 'org.jetbrains.dokka' group = 'com.github.TangemCash' version '0.1.0' @@ -9,6 +10,8 @@ dependencies { implementation "com.madgag.spongycastle:core:1.56.0.0" implementation "com.madgag.spongycastle:prov:1.56.0.0" implementation 'net.i2p.crypto:eddsa:0.3.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' + testImplementation "com.google.truth:truth:1.0" } sourceCompatibility = "8" @@ -16,15 +19,19 @@ targetCompatibility = "8" buildscript { ext.kotlin_version = '1.3.50' + ext.dokka_version = '0.10.0' repositories { mavenCentral() + jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" } } repositories { mavenCentral() + jcenter() } compileKotlin { kotlinOptions { @@ -36,3 +43,7 @@ compileTestKotlin { jvmTarget = "1.8" } } + +task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { + outputFormat = 'markdown' +} diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index 32ba138970..90c70a00f7 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -4,8 +4,8 @@ import com.tangem.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extentions.calculateSha256 -import com.tangem.common.extentions.hexToBytes +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.hexToBytes import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvMapper import com.tangem.common.tlv.TlvTag diff --git a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt b/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt index 0835cff543..aad7f01b1b 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt @@ -3,7 +3,7 @@ package com.tangem.commands import com.tangem.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extentions.toInt +import com.tangem.common.extensions.toInt import com.tangem.common.tlv.TlvTag /** diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index ff8ad89e43..be79679f14 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -4,7 +4,7 @@ import com.tangem.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extentions.calculateSha256 +import com.tangem.common.extensions.calculateSha256 import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvMapper import com.tangem.common.tlv.TlvTag diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index 23b021fef9..a825db3efa 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -4,8 +4,8 @@ import com.tangem.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extentions.calculateSha256 -import com.tangem.common.extentions.hexToBytes +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.hexToBytes import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvMapper import com.tangem.common.tlv.TlvTag diff --git a/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt b/tangem-core/src/main/java/com/tangem/common/extensions/ByteArray.kt similarity index 82% rename from tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt rename to tangem-core/src/main/java/com/tangem/common/extensions/ByteArray.kt index cfc35a118b..928f31fa69 100644 --- a/tangem-core/src/main/java/com/tangem/common/extentions/ByteArray.kt +++ b/tangem-core/src/main/java/com/tangem/common/extensions/ByteArray.kt @@ -1,4 +1,4 @@ -package com.tangem.common.extentions +package com.tangem.common.extensions import java.nio.ByteBuffer import java.security.MessageDigest @@ -28,9 +28,9 @@ fun ByteArray.toInt(): Int { } fun ByteArray.toDate(): Date { - val year = ((this[0] and 0xFF.toByte()).toInt() shl 8) or (this[1] and 0xFF.toByte()).toInt() - val month = this[2] - 1 - val day = this[3].toInt() + val year = copyOfRange(0, 2).toInt() + val month = if (this.size > 2) this[2] - 1 else 0 + val day = if (this.size > 3) this[3].toInt() else 0 val cd = Calendar.getInstance() cd.set(year, month, day, 0, 0, 0) return cd.time diff --git a/tangem-core/src/main/java/com/tangem/common/extentions/String.kt b/tangem-core/src/main/java/com/tangem/common/extensions/String.kt similarity index 60% rename from tangem-core/src/main/java/com/tangem/common/extentions/String.kt rename to tangem-core/src/main/java/com/tangem/common/extensions/String.kt index 06a4f1a26e..2f9d73f416 100644 --- a/tangem-core/src/main/java/com/tangem/common/extentions/String.kt +++ b/tangem-core/src/main/java/com/tangem/common/extensions/String.kt @@ -1,4 +1,4 @@ -package com.tangem.common.extentions +package com.tangem.common.extensions import java.nio.charset.Charset import java.security.MessageDigest @@ -13,10 +13,8 @@ fun String.calculateSha256(): ByteArray { } fun String.hexToBytes(): ByteArray { - val bytes = ByteArray(this.length / 2) - for (i in bytes.indices) { - bytes[i] = Integer.parseInt(this.substring(2 * i, 2 * i + 2), - 16).toByte() + return ByteArray(this.length / 2) + { i -> + Integer.parseInt(this.substring(2 * i, 2 * i + 2), 16).toByte() } - return bytes } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt index 5829f0600a..fe153c9d3d 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt @@ -1,10 +1,10 @@ package com.tangem.common.tlv import com.tangem.commands.* -import com.tangem.common.extentions.toDate -import com.tangem.common.extentions.toHexString -import com.tangem.common.extentions.toInt -import com.tangem.common.extentions.toUtf8 +import com.tangem.common.extensions.toDate +import com.tangem.common.extensions.toHexString +import com.tangem.common.extensions.toInt +import com.tangem.common.extensions.toUtf8 import java.util.* diff --git a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt index 32742a3076..734b3d8f7a 100644 --- a/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt +++ b/tangem-core/src/main/java/com/tangem/crypto/CryptoUtils.kt @@ -40,7 +40,7 @@ object CryptoUtils { fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): Boolean { return when (curve) { - EllipticCurve.Secp256k1 -> Sepc256k1.verify(publicKey, message, signature) + EllipticCurve.Secp256k1 -> Secp256k1.verify(publicKey, message, signature) EllipticCurve.Ed25519 -> Ed25519.verify(publicKey, message, signature) } } @@ -58,7 +58,7 @@ object CryptoUtils { curve: EllipticCurve = EllipticCurve.Secp256k1 ): ByteArray { return when (curve) { - EllipticCurve.Secp256k1 -> Sepc256k1.generatePublicKey(privateKeyArray) + EllipticCurve.Secp256k1 -> Secp256k1.generatePublicKey(privateKeyArray) EllipticCurve.Ed25519 -> Ed25519.generatePublicKey(privateKeyArray) } } @@ -74,8 +74,8 @@ object CryptoUtils { */ fun ByteArray.sign(privateKeyArray: ByteArray, curve: EllipticCurve = EllipticCurve.Secp256k1): ByteArray { return when (curve) { - EllipticCurve.Secp256k1 -> signSecp256k1(this, privateKeyArray) - EllipticCurve.Ed25519 -> signEd25519(this, privateKeyArray) + EllipticCurve.Secp256k1 -> Secp256k1.sign(this, privateKeyArray) + EllipticCurve.Ed25519 -> Ed25519.sign(this, privateKeyArray) } } diff --git a/tangem-core/src/main/java/com/tangem/crypto/Ed25519.kt b/tangem-core/src/main/java/com/tangem/crypto/Ed25519.kt index 7c07cc0f68..fd0cf14cc1 100644 --- a/tangem-core/src/main/java/com/tangem/crypto/Ed25519.kt +++ b/tangem-core/src/main/java/com/tangem/crypto/Ed25519.kt @@ -1,6 +1,6 @@ package com.tangem.crypto -import com.tangem.common.extentions.calculateSha512 +import com.tangem.common.extensions.calculateSha512 import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -10,36 +10,46 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import java.security.MessageDigest import java.security.PublicKey -internal fun verifyEd25519(publicKey: ByteArray, message: ByteArray, signature: ByteArray): Boolean { - val messageSha512 = message.calculateSha512() - val loadedPublicKey = loadPublicKey(publicKey) - val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) - val signatureInstance = EdDSAEngine(MessageDigest.getInstance(spec.hashAlgorithm)) - signatureInstance.initVerify(loadedPublicKey) +object Ed25519 { - signatureInstance.update(messageSha512) + internal fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray): Boolean { + val messageSha512 = message.calculateSha512() + val loadedPublicKey = loadPublicKey(publicKey) + val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) + val signatureInstance = EdDSAEngine(MessageDigest.getInstance(spec.hashAlgorithm)) + signatureInstance.initVerify(loadedPublicKey) - return signatureInstance.verify(signature) -} + signatureInstance.update(messageSha512) -private fun loadPublicKey(publicKeyArray: ByteArray): PublicKey { - val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) - val pubKey = EdDSAPublicKeySpec(publicKeyArray, spec) - return EdDSAPublicKey(pubKey) -} + return signatureInstance.verify(signature) + } -internal fun signEd25519(data: ByteArray, privateKeyArray: ByteArray): ByteArray { + private fun loadPublicKey(publicKeyArray: ByteArray): PublicKey { + val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) + val pubKey = EdDSAPublicKeySpec(publicKeyArray, spec) + return EdDSAPublicKey(pubKey) + } - val dataSha512 = data.calculateSha512() - val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) - val signatureInstance = EdDSAEngine(MessageDigest.getInstance(spec.hashAlgorithm)) + internal fun sign(data: ByteArray, privateKeyArray: ByteArray): ByteArray { - val privateKeySpec = EdDSAPrivateKeySpec(privateKeyArray, spec) - val privateKey = EdDSAPrivateKey(privateKeySpec) + val dataSha512 = data.calculateSha512() + val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) + val signatureInstance = EdDSAEngine(MessageDigest.getInstance(spec.hashAlgorithm)) - signatureInstance.initSign(privateKey) - signatureInstance.update(dataSha512) + val privateKeySpec = EdDSAPrivateKeySpec(privateKeyArray, spec) + val privateKey = EdDSAPrivateKey(privateKeySpec) - return signatureInstance.sign() + signatureInstance.initSign(privateKey) + signatureInstance.update(dataSha512) + return signatureInstance.sign() + } + + internal fun generatePublicKey(privateKeyArray: ByteArray): ByteArray { + val spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519) + val privateKeySpec = EdDSAPrivateKeySpec(privateKeyArray, spec) + val publicKeySpec = EdDSAPublicKeySpec(privateKeySpec.a, spec) + val publicKey = EdDSAPublicKey(publicKeySpec) + return publicKey.abyte + } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/crypto/Secp256k1.kt b/tangem-core/src/main/java/com/tangem/crypto/Secp256k1.kt new file mode 100644 index 0000000000..2a62979bed --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/crypto/Secp256k1.kt @@ -0,0 +1,118 @@ +package com.tangem.crypto + +import com.tangem.common.extensions.toHexString +import org.spongycastle.asn1.ASN1EncodableVector +import org.spongycastle.asn1.ASN1Integer +import org.spongycastle.asn1.DERSequence +import org.spongycastle.jce.ECNamedCurveTable +import org.spongycastle.jce.spec.ECPrivateKeySpec +import org.spongycastle.jce.spec.ECPublicKeySpec +import java.math.BigInteger +import java.security.KeyFactory +import java.security.PublicKey +import java.security.Signature + +object Secp256k1 { + + internal fun verify(publicKey: ByteArray, message: ByteArray, signature: ByteArray): Boolean { + val signatureInstance = Signature.getInstance("SHA256withECDSA") + val loadedPublicKey = loadPublicKey(publicKey) + signatureInstance.initVerify(loadedPublicKey) + signatureInstance.update(message) + + val v = ASN1EncodableVector() + val size = signature.size / 2 + v.add(calculateR(signature, size)) + v.add(calculateS(signature, size)) + val sigDer = DERSequence(v).encoded + + return signatureInstance.verify(sigDer) + } + + private fun loadPublicKey(publicKeyArray: ByteArray): PublicKey { + + val spec = ECNamedCurveTable.getParameterSpec("secp256k1") + val factory = KeyFactory.getInstance("EC", "SC") + + val p1 = spec.curve.decodePoint(publicKeyArray) + val keySpec = ECPublicKeySpec(p1, spec) + + return factory.generatePublic(keySpec) + } + + private fun calculateR(signature: ByteArray, size: Int): ASN1Integer = + ASN1Integer(BigInteger(1, signature.copyOfRange(0, size))) + + private fun calculateS(signature: ByteArray, size: Int): ASN1Integer = + ASN1Integer(BigInteger(1, signature.copyOfRange(size, size * 2))) + + + internal fun sign(data: ByteArray, privateKeyArray: ByteArray): ByteArray { + + val spec = ECNamedCurveTable.getParameterSpec("secp256k1") + val factory = KeyFactory.getInstance("EC", "SC") + + val keySpecP = ECPrivateKeySpec(BigInteger(1, privateKeyArray), spec) + + val signature = Signature.getInstance("SHA256withECDSA") + + val privateKey = factory.generatePrivate(keySpecP) + signature.initSign(privateKey) + signature.update(data) + + val enc = signature.sign() + checkSignatureForErrors(enc) + + val res = toByte64(enc) + + if (!verify(generatePublicKey(privateKeyArray), data, res)) { + throw Exception("Signature self verify failed - ,enc:" + enc.toHexString() + ",res:" + res.toHexString()) + } + + return res + } + + private fun checkSignatureForErrors(enc: ByteArray) { + if (enc[0].toInt() != 0x30) throw Exception("bad encoding 1") + if (enc[1].toInt() and 0x80 != 0) throw Exception("unsupported length encoding 1") + if (enc[2].toInt() != 0x02) throw Exception("bad encoding 2") + if (enc[3].toInt() and 0x80 != 0) throw Exception("unsupported length encoding 2") + var rLength = enc[3].toInt() + if (enc[4 + rLength].toInt() != 0x02) throw Exception("bad encoding 3") + if (enc[5 + rLength].toInt() and 0x80 != 0) + throw Exception("unsupported length encoding 3") + } + + private fun toByte64(enc: ByteArray): ByteArray { + + var rLength = enc[3].toInt() + var sLength = enc[5 + rLength].toInt() + + val sPos = 6 + rLength + val res = ByteArray(64) + if (rLength <= 32) { + System.arraycopy(enc, 4, res, 32 - rLength, rLength) + rLength = 32 + } else if (rLength == 33 && enc[4].toInt() == 0) { + rLength-- + System.arraycopy(enc, 5, res, 0, rLength) + } else { + throw Exception("unsupported r-length - r-length:" + rLength.toString() + ",s-length:" + sLength.toString() + ",enc:" + enc.toHexString()) + } + if (sLength <= 32) { + System.arraycopy(enc, sPos, res, rLength + 32 - sLength, sLength) + sLength = 32 + } else if (sLength == 33 && enc[sPos].toInt() == 0) { + System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1) + } else { + throw Exception("unsupported s-length - r-length:" + rLength.toString() + ",s-length:" + sLength.toString() + ",enc:" + enc.toHexString()) + } + + return res + } + + internal fun generatePublicKey(privateKeyArray: ByteArray): ByteArray { + val spec = ECNamedCurveTable.getParameterSpec("secp256k1") + return spec.g.multiply(BigInteger(1, privateKeyArray)).getEncoded(false) + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/crypto/Sepc256k1.kt b/tangem-core/src/main/java/com/tangem/crypto/Sepc256k1.kt deleted file mode 100644 index af90eb6539..0000000000 --- a/tangem-core/src/main/java/com/tangem/crypto/Sepc256k1.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.tangem.crypto - -import com.tangem.common.extentions.toHexString -import org.spongycastle.asn1.ASN1EncodableVector -import org.spongycastle.asn1.ASN1Integer -import org.spongycastle.asn1.DERSequence -import org.spongycastle.jce.ECNamedCurveTable -import org.spongycastle.jce.spec.ECPrivateKeySpec -import org.spongycastle.jce.spec.ECPublicKeySpec -import java.math.BigInteger -import java.security.KeyFactory -import java.security.PublicKey -import java.security.Signature - -internal fun verifySecp256k1(publicKey: ByteArray, message: ByteArray, signature: ByteArray): Boolean { - val signatureInstance = Signature.getInstance("SHA256withECDSA") - val loadedPublicKey = loadPublicKey(publicKey) - signatureInstance.initVerify(loadedPublicKey) - signatureInstance.update(message) - - val v = ASN1EncodableVector() - val size = signature.size / 2 - v.add(calculateR(signature, size)) - v.add(calculateS(signature, size)) - val sigDer = DERSequence(v).encoded - - return signatureInstance.verify(sigDer) -} - -private fun loadPublicKey(publicKeyArray: ByteArray): PublicKey { - - val spec = ECNamedCurveTable.getParameterSpec("secp256k1") - val factory = KeyFactory.getInstance("EC", "SC") - - val p1 = spec.curve.decodePoint(publicKeyArray) - val keySpec = ECPublicKeySpec(p1, spec) - - return factory.generatePublic(keySpec) -} - -private fun calculateR(signature: ByteArray, size: Int): ASN1Integer = - ASN1Integer(BigInteger(1, signature.copyOfRange(0, size))) - -private fun calculateS(signature: ByteArray, size: Int): ASN1Integer = - ASN1Integer(BigInteger(1, signature.copyOfRange(size, size * 2))) - - -internal fun signSecp256k1(data: ByteArray, privateKeyArray: ByteArray): ByteArray { - - val spec = ECNamedCurveTable.getParameterSpec("secp256k1") - val factory = KeyFactory.getInstance("EC", "SC") - - val keySpecP = ECPrivateKeySpec(BigInteger(1, privateKeyArray), spec) - - val signature = Signature.getInstance("SHA256withECDSA") - - val privateKey = factory.generatePrivate(keySpecP) - signature.initSign(privateKey) - signature.update(data) - - val enc = signature.sign() - checkSignatureForErrors(enc) - - val res = toByte64(enc) - - if (!verifySecp256k1(generatePublicKey(privateKeyArray), data, res)) { - throw Exception("Signature self verify failed - ,enc:" + enc.toHexString() + ",res:" + res.toHexString()) - } - - return res -} - -private fun checkSignatureForErrors(enc: ByteArray) { - if (enc[0].toInt() != 0x30) throw Exception("bad encoding 1") - if (enc[1].toInt() and 0x80 != 0) throw Exception("unsupported length encoding 1") - if (enc[2].toInt() != 0x02) throw Exception("bad encoding 2") - if (enc[3].toInt() and 0x80 != 0) throw Exception("unsupported length encoding 2") - var rLength = enc[3].toInt() - if (enc[4 + rLength].toInt() != 0x02) throw Exception("bad encoding 3") - if (enc[5 + rLength].toInt() and 0x80 != 0) - throw Exception("unsupported length encoding 3") -} - -private fun toByte64(enc: ByteArray): ByteArray { - - var rLength = enc[3].toInt() - var sLength = enc[5 + rLength].toInt() - - val sPos = 6 + rLength - val res = ByteArray(64) - if (rLength <= 32) { - System.arraycopy(enc, 4, res, 32 - rLength, rLength) - rLength = 32 - } else if (rLength == 33 && enc[4].toInt() == 0) { - rLength-- - System.arraycopy(enc, 5, res, 0, rLength) - } else { - throw Exception("unsupported r-length - r-length:" + rLength.toString() + ",s-length:" + sLength.toString() + ",enc:" + enc.toHexString()) - } - if (sLength <= 32) { - System.arraycopy(enc, sPos, res, rLength + 32 - sLength, sLength) - sLength = 32 - } else if (sLength == 33 && enc[sPos].toInt() == 0) { - System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1) - } else { - throw Exception("unsupported s-length - r-length:" + rLength.toString() + ",s-length:" + sLength.toString() + ",enc:" + enc.toHexString()) - } - - return res -} - -private fun generatePublicKey(privateKeyArray: ByteArray): ByteArray { - val spec = ECNamedCurveTable.getParameterSpec("secp256k1") - return spec.g.multiply(BigInteger(1, privateKeyArray)).getEncoded(false) -} \ No newline at end of file diff --git a/tangem-core/src/test/java/com/tangem/common/extensions/ByteArrayExtensionsTest.kt b/tangem-core/src/test/java/com/tangem/common/extensions/ByteArrayExtensionsTest.kt new file mode 100644 index 0000000000..730576b898 --- /dev/null +++ b/tangem-core/src/test/java/com/tangem/common/extensions/ByteArrayExtensionsTest.kt @@ -0,0 +1,92 @@ +package com.tangem.common.extensions + +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import java.util.* + + +class ByteArrayExtensionsTest { + + @Test + fun `card Id to Hex String`() { + val hex = "cb22000000027374" + val bytes = byteArrayOf(-53, 34, 0, 0, 0, 2, 115, 116) + assertThat(bytes.toHexString()) + .matches(hex) + } + + @Test + fun `batch Id to Hex String`() { + val hex = "0029" + val bytes = byteArrayOf(0, 41) + assertThat(bytes.toHexString()) + .matches(hex) + } + + @Test + fun `curve name to Utf8`() { + val bytes = byteArrayOf(115, 101, 99, 112, 50, 53, 54, 107, 49, 0) + val expected = "secp256k1" + val converted = bytes.toUtf8() + assertThat(converted) + .matches(expected) + } + + @Test + fun `blockchain name to Utf8`() { + val bytes = byteArrayOf(69, 84, 72) + val expected = "ETH" + val converted = bytes.toUtf8() + assertThat(converted) + .matches(expected) + } + + @Test + fun `bytes to int`() { + val bytes = byteArrayOf(0, 2, 106, 3) + val expected = 158211 + assertThat(bytes.toInt()) + .isEqualTo(expected) + } + + @Test + fun `zero to int`() { + val bytes = byteArrayOf(0) + val expected = 0 + assertThat(bytes.toInt()) + .isEqualTo(expected) + } + + @Test + fun toDate() { + val bytes1 = byteArrayOf(7, -30, 7, 27) + val expected1 = Calendar.getInstance().apply { this.set(2018, 6, 27, 0, 0, 0) }.time + val converted1 = bytes1.toDate() + assertThat(converted1.toString()) + .isEqualTo(expected1.toString()) + + val bytes2 = byteArrayOf(7, -30, 7, 27, 30) + val expected2 = Calendar.getInstance().apply { this.set(2018, 6, 27, 0, 0, 0) }.time + val converted2 = bytes2.toDate() + assertThat(converted2.toString()) + .isEqualTo(expected2.toString()) + + val bytes3 = byteArrayOf(7, -30, 7) + val expected3 = Calendar.getInstance().apply { this.set(2018, 6, 0, 0, 0, 0) }.time + val converted3 = bytes3.toDate() + assertThat(converted3.toString()) + .isEqualTo(expected3.toString()) + } + + @Test + fun `calculate sha512`() { + val bytes = ByteArray(64) { 5 } + val expected = byteArrayOf( + -123, 96, 121, 57, -117, -23, -108, 57, 25, -119, -22, 97, 11, -91, + 74, -19, -88, 21, -108, -116, -100, 111, 6, -78, 114, -115, 70, -121, 29, 102, 104, 65, + -21, -68, -111, 121, -51, 109, -94, -24, -40, 108, -25, 70, -26, 61, 38, 12, -127, -34, + -77, -81, 81, -32, -89, -112, -31, -33, 91, 114, 89, 127, -123, -58) + assertThat(bytes.calculateSha512()) + .isEqualTo(expected) + } +} \ No newline at end of file diff --git a/tangem-core/src/test/java/com/tangem/common/extensions/StringExtensionsTest.kt b/tangem-core/src/test/java/com/tangem/common/extensions/StringExtensionsTest.kt new file mode 100644 index 0000000000..ba5b3add73 --- /dev/null +++ b/tangem-core/src/test/java/com/tangem/common/extensions/StringExtensionsTest.kt @@ -0,0 +1,53 @@ +package com.tangem.common.extensions + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class StringExtensionsTest { + + @Test + fun `calculate SHA 256 for default PIN 1`() { + val pin = "000000" + val expected = byteArrayOf(-111, -76, -47, 66, -126, 63, 125, 32, -59, -16, -115, -10, -111, + 34, -34, 67, -13, 95, 5, 122, -104, -115, -106, 25, -10, -45, 19, -124, -123, -55, -94, 3) + assertThat(pin.calculateSha256()) + .isEqualTo(expected) + } + + @Test + fun `calculate SHA 256 for default PIN 2`() { + val pin = "000" + val expected = byteArrayOf(42, -55, -90, 116, 106, -54, 84, 58, -8, -33, -13, -104, -108, -49, + -24, 23, 58, -5, -94, 30, -80, 28, 111, -82, 51, -43, 41, 71, 34, 40, 85, -17) + assertThat(pin.calculateSha256()) + .isEqualTo(expected) + } + + @Test + fun `calculate SHA 256 for a sample PIN 1`() { + val pin = "999999" + val expected = byteArrayOf(-109, 115, 119, -16, 86, 22, 15, -60, -79, 94, 11, 119, 12, 103, + 19, 106, 95, 3, -63, 82, 5, -76, -45, -65, -111, -126, 104, -2, -6, 44, 109, 10) + assertThat(pin.calculateSha256()) + .isEqualTo(expected) + } + + @Test + fun `calculate SHA 256 for a sample PIN 2`() { + val pin = "999" + val expected = byteArrayOf(-125, -49, -117, 96, -99, -26, 0, 54, -88, 39, 123, -48, -23, 97, + 53, 117, 27, -68, 7, -21, 35, 66, 86, -44, -74, 91, -119, 51, 96, 101, 27, -14) + assertThat(pin.calculateSha256()) + .isEqualTo(expected) + } + + @Test + fun `card ID hex to bytes`() { + val cardId = "cb22000000027374" + val expected = byteArrayOf(-53, 34, 0, 0, 0, 2, 115, 116) + assertThat(cardId.hexToBytes()) + .isEqualTo(expected) + } + + +} \ No newline at end of file diff --git a/tangem-core/src/test/java/com/tangem/crypto/CryptoUtilsTest.kt b/tangem-core/src/test/java/com/tangem/crypto/CryptoUtilsTest.kt new file mode 100644 index 0000000000..afdb6da581 --- /dev/null +++ b/tangem-core/src/test/java/com/tangem/crypto/CryptoUtilsTest.kt @@ -0,0 +1,50 @@ +package com.tangem.crypto + +import com.google.common.truth.Truth.assertThat +import com.tangem.commands.EllipticCurve +import com.tangem.crypto.CryptoUtils.generatePublicKey +import com.tangem.crypto.CryptoUtils.generateRandomBytes +import com.tangem.crypto.CryptoUtils.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + + +class CryptoUtilsTest { + + @BeforeEach + internal fun setUp() { + CryptoUtils.initCrypto() + } + + @Test + fun generateRandomBytesTest() { + val privateKey: ByteArray = generateRandomBytes(32) + assertThat(privateKey) + .hasLength(32) + assertThat(privateKey.sum()) + .isNotEqualTo(0) + } + + @Test + internal fun verifyEd25519Test() { + val verified = verifySignature_withSampleData(EllipticCurve.Ed25519) + assertThat(verified) + .isTrue() + } + + @Test + internal fun verifySecp256k1Test() { + val verified = verifySignature_withSampleData(EllipticCurve.Secp256k1) + assertThat(verified) + .isTrue() + } + + private fun verifySignature_withSampleData(curve: EllipticCurve): Boolean { + val privateKey = ByteArray(32) { 1 } + val publicKey = generatePublicKey(privateKey, curve) + val message = ByteArray(64) { 5 } + val signature = message.sign(privateKey, curve) + return verify(publicKey, message, signature, curve) + } + +} \ No newline at end of file diff --git a/tangemtest/src/main/java/com/tangem/tangemtest/MainActivity.kt b/tangemtest/src/main/java/com/tangem/tangemtest/MainActivity.kt index e9fcb46cfa..f7c5dd6fe9 100644 --- a/tangemtest/src/main/java/com/tangem/tangemtest/MainActivity.kt +++ b/tangemtest/src/main/java/com/tangem/tangemtest/MainActivity.kt @@ -45,7 +45,7 @@ class MainActivity : AppCompatActivity() { } } } - } + } is TaskEvent.Completion -> { if (taskEvent.error != null) { if (taskEvent.error is TaskError.UserCancelledError) { @@ -73,14 +73,8 @@ class MainActivity : AppCompatActivity() { } private fun createSampleHashes(): Array { - val hash1 = ByteArray(32) - for (i in 0 until 32) { - hash1[i] = 1 - } - val hash2 = ByteArray(32) - for (i in 0 until 32) { - hash2[i] = 2 - } + val hash1 = ByteArray(32) { 1 } + val hash2 = ByteArray(32) { 2 } return arrayOf(hash1, hash2) } } \ No newline at end of file