Updated on 2026-08-14
This commit is contained in:
parent
4c64438643
commit
66a56a7344
16 changed files with 383 additions and 172 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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.*
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
118
tangem-core/src/main/java/com/tangem/crypto/Secp256k1.kt
Normal file
118
tangem-core/src/main/java/com/tangem/crypto/Secp256k1.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue