Updated on 2026-08-14

This commit is contained in:
Tangem 2020-06-11 14:40:53 +03:00
parent d60a65a57d
commit 1357ddf95d
12 changed files with 71 additions and 46 deletions

View file

@ -5,13 +5,10 @@ import com.tangem.commands.OpenSessionCommand
import com.tangem.commands.ReadCommand
import com.tangem.common.CompletionResult
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.Instruction
import com.tangem.common.apdu.ResponseApdu
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.getType
import com.tangem.crypto.EncryptionHelper
import com.tangem.crypto.FastEncryptionHelper
import com.tangem.crypto.StrongEncryptionHelper
import com.tangem.crypto.pbkdf2Hash
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@ -222,9 +219,10 @@ class CardSession(
scope.launch {
subscription.consumeAsFlow()
.filterNotNull()
.map { establishEncryption(apdu.ins) }
.map { establishEncryptionIfNeeded() }
.map { apdu.encrypt(environment.encryptionMode, environment.encryptionKey) }
.map { encryptedApdu -> reader.transceiveApdu(encryptedApdu) }
.map { responseApdu -> decrypt(responseApdu) }
.catch { if (it is TangemSdkError) callback(CompletionResult.Failure(it)) }
.collect { result ->
subscription.cancel()
@ -233,21 +231,14 @@ class CardSession(
}
}
private suspend fun establishEncryption(ins: Int): CompletionResult<Boolean> {
if (environment.encryptionKey != null) return CompletionResult.Success(true)
if (ins == Instruction.Personalize.code) {
environment.encryptionKey = null
private suspend fun establishEncryptionIfNeeded(): CompletionResult<Boolean> {
if (environment.encryptionMode == EncryptionMode.NONE || environment.encryptionKey != null) {
return CompletionResult.Success(true)
}
val encryptionHelper: EncryptionHelper =
when (environment.encryptionMode) {
EncryptionMode.NONE -> return CompletionResult.Success(true)
EncryptionMode.FAST -> FastEncryptionHelper()
EncryptionMode.STRONG -> StrongEncryptionHelper()
}
val encryptionHelper = EncryptionHelper.create(environment.encryptionMode)
?: return CompletionResult.Success(true)
val openSesssionCommand = OpenSessionCommand(encryptionHelper.keyA)
val apdu = openSesssionCommand.serialize(environment)
@ -269,4 +260,19 @@ class CardSession(
is CompletionResult.Failure -> return CompletionResult.Failure(response.error)
}
}
private fun decrypt(result: CompletionResult<ResponseApdu>): CompletionResult<ResponseApdu> {
return when (result) {
is CompletionResult.Success -> {
try {
CompletionResult.Success(
result.data.decrypt(environment.encryptionKey)
)
} catch (error: TangemSdkError) {
return CompletionResult.Failure(error)
}
}
is CompletionResult.Failure -> result
}
}
}

View file

@ -46,7 +46,7 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
open fun performPreCheck(card: Card): TangemSdkError? = null
open fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? = error
open fun mapError(card: Card?, error: TangemSdkError): TangemSdkError = error
fun transceive(session: CardSession, callback: (result: CompletionResult<T>) -> Unit) {
@ -63,10 +63,10 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
when (result) {
is CompletionResult.Failure -> {
if (session.environment.handleErrors) {
performAfterCheck(session.environment.card, result.error)?.let {
callback(CompletionResult.Failure(it))
return@transceiveApdu
}
val error = mapError(session.environment.card, result.error)
callback(CompletionResult.Failure(error))
return@transceiveApdu
}
callback(CompletionResult.Failure(result.error))
}
@ -98,13 +98,7 @@ abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRu
when (responseApdu.statusWord) {
StatusWord.ProcessCompleted, StatusWord.Pin1Changed,
StatusWord.Pin2Changed, StatusWord.PinsChanged -> {
try {
val decryptedResponseApdu =
responseApdu.decrypt(session.environment.encryptionKey)
callback(CompletionResult.Success(decryptedResponseApdu))
} catch (error: TangemSdkError) {
callback(CompletionResult.Failure(error))
}
callback(CompletionResult.Success(responseApdu))
}
StatusWord.NeedPause -> {
// NeedPause is returned from the card whenever security delay is triggered.

View file

@ -51,11 +51,11 @@ class CreateWalletCommand : Command<CreateWalletResponse>() {
}
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams) {
return TangemSdkError.Pin2OrCvcRequired()
}
return null
return error
}
override fun serialize(environment: SessionEnvironment): CommandApdu {

View file

@ -48,11 +48,11 @@ class PurgeWalletCommand : Command<PurgeWalletResponse>() {
}
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams) {
return TangemSdkError.Pin2OrCvcRequired()
}
return null
return error
}

View file

@ -367,11 +367,11 @@ class Card(
*/
class ReadCommand : Command<Card>() {
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams) {
return TangemSdkError.Pin1Required()
}
return null
return error
}
override fun serialize(environment: SessionEnvironment): CommandApdu {

View file

@ -63,11 +63,11 @@ class SignCommand(private val hashes: Array<ByteArray>) : Command<SignResponse>(
}
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams) {
return TangemSdkError.Pin2OrCvcRequired()
}
return null
return error
}
override fun serialize(environment: SessionEnvironment): CommandApdu {

View file

@ -60,11 +60,11 @@ class WriteIssuerDataCommand(
return null
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams && isCounterRequired(card)) {
return TangemSdkError.DataCannotBeWritten()
}
return null
return error
}
private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean =

View file

@ -72,7 +72,7 @@ class WriteIssuerExtraDataCommand(
return null
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams && isCounterRequired(card)) {
return TangemSdkError.DataCannotBeWritten()
}
@ -80,7 +80,7 @@ class WriteIssuerExtraDataCommand(
card?.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) == true) {
return TangemSdkError.OverwritingDataIsProhibited()
}
return null
return error
}
private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean =
@ -134,7 +134,7 @@ class WriteIssuerExtraDataCommand(
}
is CompletionResult.Failure -> {
if (session.environment.handleErrors) {
performAfterCheck(session.environment.card, result.error)?.let {
mapError(session.environment.card, result.error)?.let {
callback(CompletionResult.Failure(it))
}
}

View file

@ -45,11 +45,11 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val
return null
}
override fun performAfterCheck(card: Card?, error: TangemSdkError): TangemSdkError? {
override fun mapError(card: Card?, error: TangemSdkError): TangemSdkError {
if (error is TangemSdkError.InvalidParams) {
return TangemSdkError.Pin2OrCvcRequired()
}
return null
return error
}
override fun serialize(environment: SessionEnvironment): CommandApdu {

View file

@ -1,5 +1,6 @@
package com.tangem.commands.personalization
import com.tangem.CardSession
import com.tangem.EncryptionMode
import com.tangem.SessionEnvironment
import com.tangem.TangemSdkError
@ -9,6 +10,7 @@ import com.tangem.commands.CardStatus
import com.tangem.commands.Command
import com.tangem.commands.common.CardDeserializer
import com.tangem.commands.personalization.entities.*
import com.tangem.common.CompletionResult
import com.tangem.common.apdu.CommandApdu
import com.tangem.common.apdu.Instruction
import com.tangem.common.apdu.ResponseApdu
@ -37,6 +39,18 @@ class PersonalizeCommand(
private val acquirer: Acquirer? = null
) : Command<Card>() {
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
val encryptionMode = session.environment.encryptionMode
val encryptionKey = session.environment.encryptionKey
session.environment.encryptionMode = EncryptionMode.NONE
session.environment.encryptionKey = devPersonalizationKey
super.run(session) { result ->
session.environment.encryptionMode = encryptionMode
session.environment.encryptionKey = encryptionKey
callback(result)
}
}
override fun performPreCheck(card: Card): TangemSdkError? {
if (card.status != CardStatus.NotPersonalized) {
return TangemSdkError.AlreadyPersonalized()
@ -46,11 +60,10 @@ class PersonalizeCommand(
override fun serialize(environment: SessionEnvironment): CommandApdu {
return CommandApdu(Instruction.Personalize, serializePersonalizationData(config))
.encrypt(EncryptionMode.NONE, devPersonalizationKey)
}
override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card {
return CardDeserializer.deserialize(apdu.decrypt(devPersonalizationKey))
return CardDeserializer.deserialize(apdu)
}
private fun serializePersonalizationData(config: CardConfig): ByteArray {

View file

@ -39,7 +39,8 @@ class ResponseApdu(private val data: ByteArray) {
fun decrypt(encryptionKey: ByteArray?): ResponseApdu {
if (encryptionKey == null) return this
if (data.size < 18) throw TangemSdkError.InvalidResponse()
//nothing to decrypt
if (data.size < 18) return this
val responseData = data.copyOf(data.size - 2)

View file

@ -1,5 +1,6 @@
package com.tangem.crypto
import com.tangem.EncryptionMode
import org.spongycastle.jce.interfaces.ECPublicKey
import java.security.KeyPair
import java.security.KeyPairGenerator
@ -11,6 +12,16 @@ interface EncryptionHelper {
val keyA: ByteArray
fun generateSecret(keyB: ByteArray): ByteArray
companion object {
fun create(encryptionMode: EncryptionMode): EncryptionHelper? {
return when (encryptionMode) {
EncryptionMode.NONE -> null
EncryptionMode.FAST -> FastEncryptionHelper()
EncryptionMode.STRONG -> StrongEncryptionHelper()
}
}
}
}
class StrongEncryptionHelper : EncryptionHelper {