diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt index a7e6113daa..8211251100 100644 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ b/tangem-core/src/main/java/com/tangem/CardManager.kt @@ -113,7 +113,7 @@ class CardManager( */ fun readIssuerExtraData(cardId: String, callback: (result: TaskEvent) -> Unit) { - val task = ReadIssuerExtraDataTask() + val task = ReadIssuerExtraDataTask(config.issuerPublicKey) runTask(task, cardId, callback) } @@ -171,6 +171,7 @@ class CardManager( val task = WriteIssuerExtraDataTask( issuerData, startingSignature, finalizingSignature, + config.issuerPublicKey, issuerDataCounter ) runTask(task, cardId, callback) diff --git a/tangem-core/src/main/java/com/tangem/Config.kt b/tangem-core/src/main/java/com/tangem/Config.kt index 7689e77697..2d1193e176 100644 --- a/tangem-core/src/main/java/com/tangem/Config.kt +++ b/tangem-core/src/main/java/com/tangem/Config.kt @@ -1,5 +1,6 @@ package com.tangem -data class Config( - val linkedTerminal: Boolean = true +class Config( + val linkedTerminal: Boolean = true, + val issuerPublicKey: ByteArray? = null ) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/IssuerDataVerifier.kt b/tangem-core/src/main/java/com/tangem/commands/IssuerDataVerifier.kt new file mode 100644 index 0000000000..cdc7bc4246 --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/IssuerDataVerifier.kt @@ -0,0 +1,40 @@ +package com.tangem.commands + +import com.tangem.common.tlv.TlvEncoder +import com.tangem.common.tlv.TlvTag +import com.tangem.crypto.CryptoUtils +import java.io.ByteArrayOutputStream + +interface IssuerDataVerifier { + fun verify( + issuerPublicKey: ByteArray, signature: ByteArray, issuerDataToVerify: IssuerDataToVerify + ): Boolean +} + +class IssuerDataToVerify( + val cardId: String, + val issuerData: ByteArray?, + val issuerDataCounter: Int? = null, + val issuerExtraDataSize: Int? = null +) + +class DefaultIssuerDataVerifier : IssuerDataVerifier { + override fun verify( + issuerPublicKey: ByteArray, + signature: ByteArray, + issuerDataToVerify: IssuerDataToVerify + ): Boolean { + + val tlvEncoder = TlvEncoder() + val dataToVerify = ByteArrayOutputStream() + dataToVerify.write(tlvEncoder.encodeValue(TlvTag.CardId, issuerDataToVerify.cardId)) + issuerDataToVerify.issuerData?.let { dataToVerify.write(it) } + issuerDataToVerify.issuerDataCounter?.let { counter -> + dataToVerify.write(tlvEncoder.encodeValue(TlvTag.IssuerDataCounter, counter)) + } + issuerDataToVerify.issuerExtraDataSize?.let { + dataToVerify.write(tlvEncoder.encodeValue(TlvTag.Size, it)) + } + return CryptoUtils.verify(issuerPublicKey, dataToVerify.toByteArray(), signature) + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index bbec2e5fb0..fc33d22b41 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -7,7 +7,6 @@ import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder import com.tangem.common.tlv.TlvMapper import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.ReadIssuerExtraDataTask import com.tangem.tasks.TaskError class ReadIssuerExtraDataResponse( @@ -53,7 +52,9 @@ class ReadIssuerExtraDataResponse( * biometric information for ID card product. Because of the large size of Issuer_Extra_Data, * a series of these commands have to be executed to read the entire Issuer_Extra_Data. */ -class ReadIssuerExtraDataCommand : CommandSerializer() { +class ReadIssuerExtraDataCommand( + verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() +) : CommandSerializer(), IssuerDataVerifier by verifier { var offset: Int = 0 diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 039ccff6dd..70e7f3064c 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -40,8 +40,9 @@ class WriteIssuerExtraDataCommand( private val issuerData: ByteArray, private val startingSignature: ByteArray, private val finalizingSignature: ByteArray, - private val issuerDataCounter: Int? = null -) : CommandSerializer() { + private val issuerDataCounter: Int? = null, + verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() +) : CommandSerializer(), IssuerDataVerifier by verifier { var mode: IssuerDataMode = IssuerDataMode.ExtraData var offset: Int = 0 diff --git a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt index cd8b8738a0..cf3cd25986 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt @@ -3,20 +3,19 @@ package com.tangem.tasks import com.tangem.commands.* import com.tangem.common.CardEnvironment import com.tangem.common.CompletionResult -import com.tangem.common.tlv.TlvEncoder -import com.tangem.common.tlv.TlvTag -import com.tangem.crypto.CryptoUtils import java.io.ByteArrayOutputStream /** * This task performs [ReadIssuerExtraDataCommand] repeatedly until * the issuer extra data is fully retrieved. */ -internal class ReadIssuerExtraDataTask : Task() { +internal class ReadIssuerExtraDataTask( + private val issuerPublicKey: ByteArray?) : Task() { private val issuerData = ByteArrayOutputStream() private var issuerDataSize = 0 private lateinit var card: Card + private lateinit var cardEnvironment: CardEnvironment override fun onRun( cardEnvironment: CardEnvironment, @@ -24,13 +23,13 @@ internal class ReadIssuerExtraDataTask : Task() { callback: (result: TaskEvent) -> Unit ) { card = currentCard!! + this.cardEnvironment = cardEnvironment val command = ReadIssuerExtraDataCommand() - readIssuerData(command, cardEnvironment, callback) + readIssuerData(command, callback) } private fun readIssuerData( command: ReadIssuerExtraDataCommand, - cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { if (issuerDataSize != 0) { @@ -45,8 +44,9 @@ internal class ReadIssuerExtraDataTask : Task() { is CompletionResult.Success -> { if (result.data.size != null) { if (result.data.size == 0) { - completeNfcSession(TaskError.NoData()) - callback(TaskEvent.Completion(TaskError.NoData())) + completeNfcSession() + callback(TaskEvent.Event(result.data)) + callback(TaskEvent.Completion()) return@sendCommand } issuerDataSize = result.data.size @@ -54,9 +54,9 @@ internal class ReadIssuerExtraDataTask : Task() { issuerData.write(result.data.issuerData) if (result.data.issuerDataSignature == null) { command.offset = issuerData.size() - readIssuerData(command, cardEnvironment, callback) + readIssuerData(command, callback) } else { - completeTask(result.data, callback) + completeTask(result.data, command, callback) } } is CompletionResult.Failure -> { @@ -69,9 +69,15 @@ internal class ReadIssuerExtraDataTask : Task() { } } - private fun completeTask(data: ReadIssuerExtraDataResponse, + private fun completeTask(data: ReadIssuerExtraDataResponse, command: ReadIssuerExtraDataCommand, callback: (result: TaskEvent) -> Unit) { - if (isIssuerDataValid(data.issuerDataCounter, data.issuerDataSignature!!)) { + val publicKey = issuerPublicKey ?: card.issuerPublicKey!! + val dataToVerify = IssuerDataToVerify( + cardEnvironment.cardId!!, + issuerData.toByteArray(), + data.issuerDataCounter + ) + if (command.verify(publicKey, data.issuerDataSignature!!, dataToVerify)) { completeNfcSession() val finalResult = ReadIssuerExtraDataResponse( data.cardId, @@ -87,21 +93,4 @@ internal class ReadIssuerExtraDataTask : Task() { callback(TaskEvent.Completion(TaskError.VerificationFailed())) } } - - private fun isIssuerDataValid(issuerDataCounter: Int?, signature: ByteArray): Boolean { - val tlvEncoder = TlvEncoder() - val dataToVerify = ByteArrayOutputStream() - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.CardId, card.cardId)) - dataToVerify.write(issuerData.toByteArray()) - if (counterIncludedInSignature(issuerDataCounter)) { - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.IssuerDataCounter, issuerDataCounter)) - } - return CryptoUtils.verify(card.issuerPublicKey!!, dataToVerify.toByteArray(), signature) - } - - private fun counterIncludedInSignature(issuerDataCounter: Int?): Boolean { - return issuerDataCounter != null && - card.settingsMask?.contains(SettingsMask.protectIssuerDataAgainstReplay) != false - } - } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/Task.kt b/tangem-core/src/main/java/com/tangem/tasks/Task.kt index 543100d210..2e193ecc5e 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/Task.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/Task.kt @@ -1,6 +1,5 @@ package com.tangem.tasks -import com.tangem.common.CardEnvironment import com.tangem.CardManagerDelegate import com.tangem.CardReader import com.tangem.Log @@ -8,6 +7,7 @@ import com.tangem.commands.Card import com.tangem.commands.CommandResponse import com.tangem.commands.CommandSerializer import com.tangem.commands.ReadCommand +import com.tangem.common.CardEnvironment import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.StatusWord @@ -52,9 +52,8 @@ sealed class TaskError(val code: Int): Exception() { class UnknownError: TaskError(6000) - //Input Data Errors + //Issuer Data Errors class MissingCounter: TaskError(7001) - class NoData: TaskError(7002) } /** diff --git a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt index e5556d8bd1..ba3a510f75 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt @@ -3,10 +3,6 @@ package com.tangem.tasks import com.tangem.commands.* import com.tangem.common.CardEnvironment import com.tangem.common.CompletionResult -import com.tangem.common.tlv.TlvEncoder -import com.tangem.common.tlv.TlvTag -import com.tangem.crypto.CryptoUtils -import java.io.ByteArrayOutputStream /** * This task performs [WriteIssuerExtraDataCommand] repeatedly until the issuer extra data is fully @@ -24,32 +20,35 @@ internal class WriteIssuerExtraDataTask( private val issuerData: ByteArray, private val startingSignature: ByteArray, private val finalizingSignature: ByteArray, + private val issuerPublicKey: ByteArray? = null, private val issuerDataCounter: Int? = null ) : Task() { private lateinit var card: Card + private lateinit var cardEnvironment: CardEnvironment override fun onRun(cardEnvironment: CardEnvironment, currentCard: Card?, callback: (result: TaskEvent) -> Unit) { card = currentCard!! - if (!isCounterValid(issuerDataCounter)) { - completeNfcSession(TaskError.MissingCounter()) - callback(TaskEvent.Completion(TaskError.MissingCounter())) - } else if (!verifySignatures()) { - completeNfcSession(TaskError.VerificationFailed()) - callback(TaskEvent.Completion(TaskError.VerificationFailed())) - } + this.cardEnvironment = cardEnvironment val command = WriteIssuerExtraDataCommand( issuerData, startingSignature, finalizingSignature, issuerDataCounter ) - writeIssuerData(command, cardEnvironment, callback) + if (!isCounterValid(issuerDataCounter)) { + completeNfcSession(TaskError.MissingCounter()) + callback(TaskEvent.Completion(TaskError.MissingCounter())) + } else if (!verifySignatures(command)) { + completeNfcSession(TaskError.VerificationFailed()) + callback(TaskEvent.Completion(TaskError.VerificationFailed())) + } + + writeIssuerData(command, callback) } private fun writeIssuerData( command: WriteIssuerExtraDataCommand, - cardEnvironment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { if (command.mode == IssuerDataMode.WriteExtraData) { @@ -62,7 +61,7 @@ internal class WriteIssuerExtraDataTask( when (command.mode) { IssuerDataMode.ExtraData -> { command.mode = IssuerDataMode.WriteExtraData - writeIssuerData(command, cardEnvironment, callback) + writeIssuerData(command, callback) return@sendCommand } IssuerDataMode.WriteExtraData -> { @@ -70,7 +69,7 @@ internal class WriteIssuerExtraDataTask( if (command.offset >= issuerData.size) { command.mode = IssuerDataMode.FinalizeExtraData } - writeIssuerData(command, cardEnvironment, callback) + writeIssuerData(command, callback) return@sendCommand } IssuerDataMode.FinalizeExtraData -> { @@ -96,32 +95,14 @@ internal class WriteIssuerExtraDataTask( private fun isCounterRequired(): Boolean = card.settingsMask?.contains(SettingsMask.protectIssuerDataAgainstReplay) != false - private fun verifySignatures(): Boolean = verifyFirstSignature() && verifyFinalizingSignature() + private fun verifySignatures(command: WriteIssuerExtraDataCommand): Boolean { + val publicKey = issuerPublicKey ?: card.issuerPublicKey!! + val cardId = cardEnvironment.cardId!! - private fun verifyFirstSignature(): Boolean { - val tlvEncoder = TlvEncoder() - val dataToVerify = ByteArrayOutputStream() - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.CardId, card.cardId)) - if (isCounterRequired()) { - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.IssuerDataCounter, issuerDataCounter)) - } - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.Size, issuerData.size)) + val firstData = IssuerDataToVerify(cardId, null, issuerDataCounter, issuerData.size) + val secondData = IssuerDataToVerify(cardId, issuerData, issuerDataCounter) - return CryptoUtils.verify( - card.issuerPublicKey!!, dataToVerify.toByteArray(), startingSignature - ) - } - - private fun verifyFinalizingSignature(): Boolean { - val tlvEncoder = TlvEncoder() - val dataToVerify = ByteArrayOutputStream() - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.CardId, card.cardId)) - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.IssuerData, issuerData)) - if (isCounterRequired()) { - dataToVerify.write(tlvEncoder.encodeValue(TlvTag.IssuerDataCounter, issuerDataCounter)) - } - return CryptoUtils.verify( - card.issuerPublicKey!!, dataToVerify.toByteArray(), finalizingSignature - ) + return command.verify(publicKey, startingSignature, firstData) && + command.verify(publicKey, finalizingSignature, secondData) } } \ No newline at end of file