Updated on 2026-08-14

This commit is contained in:
Tangem 2019-11-18 11:35:11 +03:00
parent 4c64438643
commit 66a56a7344
16 changed files with 383 additions and 172 deletions

View file

@ -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

View file

@ -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
/**

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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.*

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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)
}