Updated on 2026-08-14
This commit is contained in:
commit
6eed5da249
29 changed files with 251 additions and 49 deletions
|
|
@ -46,7 +46,7 @@ android {
|
|||
applicationIdSuffix ".debug_beta"
|
||||
buildConfigField 'boolean', 'CRASHLYTICS', 'Boolean.parseBoolean("true")'
|
||||
manifestPlaceholders = [fabric_api_key: properties.hasProperty('fabric_api_key')
|
||||
? project.property('fabric_api_key') : 000]
|
||||
? project.property('fabric_api_key') : '000']
|
||||
}
|
||||
}
|
||||
flavorDimensions ""
|
||||
|
|
@ -91,23 +91,22 @@ dependencies {
|
|||
implementation project(':tangem-card')
|
||||
implementation project(':tangem-sdk')
|
||||
implementation project(':server-android')
|
||||
// implementation 'com.github.TangemCash:card-common:0.1.2'
|
||||
// implementation 'com.github.TangemCash:card-android:0.1.3'
|
||||
// implementation 'com.github.TangemCash:server-android:0.1.4'
|
||||
implementation 'android.arch.navigation:navigation-fragment:1.0.0'
|
||||
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0'
|
||||
|
||||
implementation 'androidx.navigation:navigation-fragment:2.1.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime:2.0.0"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.0.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
|
||||
implementation 'androidx.fragment:fragment:1.1.0-rc04'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime:2.1.0"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.fragment:fragment:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation "androidx.preference:preference:1.1.0-rc01"
|
||||
implementation 'androidx.biometric:biometric:1.0.0-alpha04'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha09'
|
||||
implementation 'androidx.core:core-ktx:1.0.2'
|
||||
implementation "androidx.preference:preference:1.1.0"
|
||||
implementation 'androidx.biometric:biometric:1.0.0-beta01'
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha10'
|
||||
|
||||
implementation 'com.google.dagger:dagger:2.21'
|
||||
kapt 'com.google.dagger:dagger-compiler:2.21'
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
|
||||
|
|
|
|||
|
|
@ -97,4 +97,6 @@ object Constant {
|
|||
const val REQUEST_CODE_SCAN_QR_SECRET = "REQUEST_CODE_SCAN_QR_SECRET"
|
||||
const val REQUEST_CODE_SCAN_QR_USER_ID = "REQUEST_CODE_SCAN_QR_USER_ID"
|
||||
|
||||
const val TERMINAL_PRIVATE_KEY = "terminalPrivateKey"
|
||||
const val TERMINAL_PUBLIC_KEY = "terminalPublicKey"
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@ package com.tangem.data.dp
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
import com.orhanobut.hawk.Hawk
|
||||
import com.tangem.Constant
|
||||
import com.tangem.tangem_card.reader.CardCrypto
|
||||
|
||||
class PrefsManager {
|
||||
companion object {
|
||||
|
|
@ -47,4 +47,18 @@ class PrefsManager {
|
|||
}
|
||||
|
||||
fun getAllCids(): String = Hawk.get("CID_Key", "")
|
||||
|
||||
val terminalKeys: Map<String, ByteArray>
|
||||
get() {
|
||||
val privateKey = Hawk.get<ByteArray>(Constant.TERMINAL_PRIVATE_KEY) ?: byteArrayOf()
|
||||
val publicKey = Hawk.get<ByteArray>(Constant.TERMINAL_PUBLIC_KEY) ?: byteArrayOf()
|
||||
return if (privateKey.isNotEmpty() && publicKey.isNotEmpty()) {
|
||||
mapOf(Constant.TERMINAL_PRIVATE_KEY to privateKey, Constant.TERMINAL_PUBLIC_KEY to publicKey)
|
||||
} else {
|
||||
val keys = CardCrypto.generateTerminalKeys()
|
||||
Hawk.put(Constant.TERMINAL_PRIVATE_KEY, keys[Constant.TERMINAL_PRIVATE_KEY])
|
||||
Hawk.put(Constant.TERMINAL_PUBLIC_KEY, keys[Constant.TERMINAL_PUBLIC_KEY])
|
||||
keys
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,14 @@ package com.tangem.data.network;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.data.network.model.AdaliteBody;
|
||||
import com.tangem.data.network.model.AdaliteResponse;
|
||||
import com.tangem.data.network.model.AdaliteResponseUtxo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.data.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.App;
|
||||
import com.tangem.data.network.model.InfuraBody;
|
||||
import com.tangem.data.network.model.InfuraResponse;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package com.tangem.data.network;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.data.network.model.InsightBody;
|
||||
import com.tangem.data.network.model.InsightResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.data.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.App;
|
||||
import com.tangem.data.network.model.InfuraBody;
|
||||
import com.tangem.data.network.model.InfuraResponse;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.data.network;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.App;
|
||||
import com.tangem.data.network.model.InfuraBody;
|
||||
import com.tangem.data.network.model.InfuraResponse;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ package com.tangem.data.network;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.tangem.App;
|
||||
import com.tangem.data.Blockchain;
|
||||
import com.tangem.data.network.model.SoChain;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.tangem_card.data.TangemCard
|
|||
import com.tangem.tangem_card.reader.CardProtocol
|
||||
import com.tangem.tangem_card.tasks.CustomReadCardTask
|
||||
import com.tangem.tangem_card.tasks.ReadCardInfoTask
|
||||
import com.tangem.tangem_sdk.android.data.PINStorage
|
||||
import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation
|
||||
import com.tangem.tangem_sdk.android.reader.NfcReader
|
||||
import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD
|
||||
|
|
@ -129,6 +130,10 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
|
|||
|
||||
lastTag = tag
|
||||
|
||||
val terminalKeys = viewModel.getTerminalKeys()
|
||||
PINStorage.setTerminalPrivateKey(terminalKeys[Constant.TERMINAL_PRIVATE_KEY])
|
||||
PINStorage.setTerminalPublicKey(terminalKeys[Constant.TERMINAL_PUBLIC_KEY])
|
||||
|
||||
task = ReadCardInfoTask(NfcReader((activity as MainActivity).nfcManager, isoDep),
|
||||
App.localStorage, App.pinStorage, this)
|
||||
task?.start()
|
||||
|
|
@ -166,6 +171,10 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader
|
|||
val card = TangemCard(uid)
|
||||
cardInfo.getBundle(EXTRA_TANGEM_CARD)?.let { card.loadFromBundle(it) }
|
||||
|
||||
val terminalKeys = viewModel.getTerminalKeys()
|
||||
card.terminalPrivateKey = terminalKeys[Constant.TERMINAL_PRIVATE_KEY]
|
||||
card.terminalPublicKey = terminalKeys[Constant.TERMINAL_PUBLIC_KEY]
|
||||
|
||||
val ctx = TangemContext(card)
|
||||
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.Constant
|
||||
import com.tangem.data.dp.PrefsManager
|
||||
import com.tangem.data.network.ServerApiCommon
|
||||
import com.tangem.wallet.BuildConfig
|
||||
|
||||
|
|
@ -35,4 +36,9 @@ class MainViewModel : ViewModel() {
|
|||
}
|
||||
serverApiCommon.requestLastVersion()
|
||||
}
|
||||
|
||||
fun getTerminalKeys(): Map<String, ByteArray> {
|
||||
return PrefsManager.getInstance().terminalKeys
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -354,8 +354,13 @@ class VerifyCardFragment : BaseFragment(), NavigationResultListener, NfcAdapter.
|
|||
if (ctx.card!!.supportBlock()!!)
|
||||
features += getString(R.string.details_blockable)
|
||||
|
||||
if (ctx.card!!.supportLinkingTerminal()) {
|
||||
features += getString(R.string.details_linking_card_supported)
|
||||
llLinkedCard.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (ctx.card!!.supportOnlyOneCommandAtTime()!!)
|
||||
|
||||
if (ctx.card!!.supportOnlyOneCommandAtTime())
|
||||
features += getString(R.string.details_atomic_commmands)
|
||||
|
||||
if (features.endsWith("\n"))
|
||||
|
|
@ -363,6 +368,13 @@ class VerifyCardFragment : BaseFragment(), NavigationResultListener, NfcAdapter.
|
|||
|
||||
tvFeatures.text = features
|
||||
|
||||
val textIsLinked = if (ctx.card.terminalIsLinked) {
|
||||
getString(R.string.details_linked_card_to_phone)
|
||||
} else {
|
||||
getString(R.string.general_no)
|
||||
}
|
||||
tvIsLinked.text = textIsLinked
|
||||
|
||||
if (ctx.card!!.useDefaultPIN1()) {
|
||||
imgPIN.setImageResource(R.drawable.unlock_pin1)
|
||||
imgPIN.setOnClickListener { Toast.makeText(context, R.string.details_protected_by_default_pin_1, Toast.LENGTH_LONG).show() }
|
||||
|
|
|
|||
|
|
@ -595,14 +595,14 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte
|
|||
}
|
||||
|
||||
if (ctx.message == null || ctx.message.isEmpty()) {
|
||||
tvMessage.text = ""
|
||||
tvMessage.visibility = View.GONE
|
||||
tvMessage?.text = ""
|
||||
tvMessage?.visibility = View.GONE
|
||||
} else {
|
||||
tvMessage.text = ctx.message
|
||||
tvMessage.visibility = View.VISIBLE
|
||||
tvMessage?.text = ctx.message
|
||||
tvMessage?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (tvError.visibility == View.VISIBLE || tvMessage.visibility == View.VISIBLE) {
|
||||
if (tvError?.visibility == View.VISIBLE || tvMessage?.visibility == View.VISIBLE) {
|
||||
timerHideErrorAndMessage = Timer()
|
||||
timerHideErrorAndMessage!!.schedule(
|
||||
timerTask {
|
||||
|
|
|
|||
|
|
@ -189,6 +189,35 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llLinkedCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="@dimen/verify_card_left_col_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:fontFamily="@font/maax"
|
||||
android:text="The card is linked"
|
||||
android:textSize="@dimen/text_size_1_small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIsLinked"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/maax"
|
||||
android:textColor="@color/primary_dark"
|
||||
android:textSize="@dimen/text_size_1_small"
|
||||
android:textStyle="normal|bold"
|
||||
tools:text="To this phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -253,7 +253,10 @@
|
|||
<string name="details_dynamic_ndef">NDEF dynamique pour iOS\n</string>
|
||||
<string name="details_ndef">NDEF\n</string>
|
||||
<string name="details_blockable">Bloquable\n</string>
|
||||
<string name="details_atomic_commmands">Mode de commande atomique</string>
|
||||
<string name="details_atomic_commmands">Mode de commande atomique\n</string>
|
||||
<string name="details_linking_card_supported">La liaison au terminal est prise en charge\n</string>
|
||||
<string name="details_linked_card_title">La carte est liée</string>
|
||||
<string name="details_linked_card_to_phone">à ce téléphone</string>
|
||||
|
||||
<!-- PinSave -->
|
||||
<string name="pin_save_btn_save">Enregistrer</string>
|
||||
|
|
|
|||
|
|
@ -244,15 +244,18 @@
|
|||
<string name="details_protected_by_user_pin_2">This banknote is protected by user\'s PIN2 code</string>
|
||||
<string name="details_unlocked_banknote">Unlocked banknote, only for development use</string>
|
||||
<string name="details_security_delay">This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code</string>
|
||||
<string name="details_both_pins_can_be_changed">Allows to change PIN1 and PIN2/n</string>
|
||||
<string name="details_pin1_can_be_changed">Allows to change PIN1/n</string>
|
||||
<string name="details_pin2_can_be_changed">Allows to change PIN2/n</string>
|
||||
<string name="details_both_pins_fixed">Fixed PIN1 and PIN2/n</string>
|
||||
<string name="details_required_cvc">Requires CVC/n</string>
|
||||
<string name="details_dynamic_ndef">Dynamic NDEF for iOS/n</string>
|
||||
<string name="details_ndef">NDEF/n</string>
|
||||
<string name="details_blockable">Blockable/n</string>
|
||||
<string name="details_atomic_commmands">Atomic command mode</string>
|
||||
<string name="details_both_pins_can_be_changed">Allows to change PIN1 and PIN2\n</string>
|
||||
<string name="details_pin1_can_be_changed">Allows to change PIN1\n</string>
|
||||
<string name="details_pin2_can_be_changed">Allows to change PIN2\n</string>
|
||||
<string name="details_both_pins_fixed">Fixed PIN1 and PIN2\n</string>
|
||||
<string name="details_required_cvc">Requires CVC\n</string>
|
||||
<string name="details_dynamic_ndef">Dynamic NDEF for iOS\n</string>
|
||||
<string name="details_ndef">NDEF\n</string>
|
||||
<string name="details_blockable">Blockable\n</string>
|
||||
<string name="details_atomic_commmands">Atomic command mode\n</string>
|
||||
<string name="details_linking_card_supported">Linking to the terminal is supported\n</string>
|
||||
<string name="details_linked_card_title">The card is linked</string>
|
||||
<string name="details_linked_card_to_phone">To this phone</string>
|
||||
|
||||
<!-- PinSave -->
|
||||
<string name="pin_save_btn_save">Save</string>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ public class TangemCard {
|
|||
private byte[] pbCardKey = null;
|
||||
private byte[] pbWalletKeyRar = null;
|
||||
private Date dtPersonalization = null;
|
||||
private byte[] terminalPrivateKey;
|
||||
private byte[] terminalPublicKey;
|
||||
private boolean terminalIsLinked = false;
|
||||
|
||||
public String getBlockchainID() {
|
||||
return blockchainID;
|
||||
|
|
@ -156,6 +159,30 @@ public class TangemCard {
|
|||
this.dtPersonalization = dtPersonalization;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPrivateKey() {
|
||||
return terminalPrivateKey;
|
||||
}
|
||||
|
||||
public void setTerminalPrivateKey(byte[] terminalPrivateKey) {
|
||||
this.terminalPrivateKey = terminalPrivateKey;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPublicKey() {
|
||||
return terminalPublicKey;
|
||||
}
|
||||
|
||||
public void setTerminalPublicKey(byte[] terminalPublicKey) {
|
||||
this.terminalPublicKey = terminalPublicKey;
|
||||
}
|
||||
|
||||
public boolean getTerminalIsLinked() {
|
||||
return terminalIsLinked;
|
||||
}
|
||||
|
||||
public void setTerminalIsLinked(boolean terminalIsLinked) {
|
||||
this.terminalIsLinked = terminalIsLinked;
|
||||
}
|
||||
|
||||
private int health = 0;
|
||||
|
||||
public int getHealth() {
|
||||
|
|
@ -350,6 +377,11 @@ public class TangemCard {
|
|||
return (settingsMask & SettingsMask.UseBlock) != 0;
|
||||
}
|
||||
|
||||
public Boolean supportLinkingTerminal() {
|
||||
if (settingsMask == null) return null;
|
||||
return (settingsMask & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0;
|
||||
}
|
||||
|
||||
public int getMaxSignatures() {
|
||||
return maxSignatures;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,7 @@ public interface PINsProvider {
|
|||
*/
|
||||
void setLastUsedPIN(String pin);
|
||||
|
||||
byte[] getTerminalPublicKey();
|
||||
byte[] getTerminalPrivateKey();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import java.security.PublicKey;
|
|||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
|
|
@ -150,7 +152,8 @@ public class CardCrypto {
|
|||
int rLength = enc[3];
|
||||
|
||||
if (enc[4 + rLength] != 0x02) throw new Exception("bad encoding 3");
|
||||
if ((enc[5 + rLength] & 0x80) != 0) throw new Exception("unsupported length encoding 3");
|
||||
if ((enc[5 + rLength] & 0x80) != 0)
|
||||
throw new Exception("unsupported length encoding 3");
|
||||
int sLength = enc[5 + rLength];
|
||||
|
||||
|
||||
|
|
@ -286,4 +289,15 @@ public class CardCrypto {
|
|||
return decryptedData;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, byte[]> generateTerminalKeys() throws Exception {
|
||||
byte[] privateKey = Util.generateRandomBytes(32);
|
||||
byte[] publicKey = GeneratePublicKey(privateKey);
|
||||
|
||||
Map<String, byte[]> keys = new HashMap<>();
|
||||
keys.put("terminalPrivateKey", privateKey);
|
||||
keys.put("terminalPublicKey", publicKey);
|
||||
return keys;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,11 @@ public class CardProtocol {
|
|||
return (DefaultPIN2.equals(pin2));
|
||||
}
|
||||
|
||||
private byte[] terminalPublicKey;
|
||||
|
||||
public void setTerminalPublicKey(byte[] terminalPubKey) {
|
||||
this.terminalPublicKey = terminalPubKey;
|
||||
}
|
||||
|
||||
private TangemCard mCard;
|
||||
private Exception mError;
|
||||
|
|
@ -488,12 +493,22 @@ public class CardProtocol {
|
|||
CommandApdu Apdu = new CommandApdu(ins);
|
||||
byte[] baPIN = Util.calculateSHA256(mPIN);
|
||||
Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN);
|
||||
if (ins != INS.Read) {
|
||||
if (ins == INS.Read) {
|
||||
addTerminalPublicKeyToApdu(Apdu);
|
||||
} else {
|
||||
Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID());
|
||||
}
|
||||
return Apdu;
|
||||
}
|
||||
|
||||
private void addTerminalPublicKeyToApdu(CommandApdu apdu) {
|
||||
if (terminalPublicKey != null) {
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, terminalPublicKey);
|
||||
} else if (mCard.getTerminalPublicKey() != null) {
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_PublicKey, mCard.getTerminalPublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Run READ command and parse answer
|
||||
// * {@see run_Read(boolean parseResult) }
|
||||
|
|
@ -633,7 +648,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public void run_CreateWallet(String PIN2) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.CreateWallet);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
|
@ -835,7 +851,8 @@ public class CardProtocol {
|
|||
CommandApdu rqApdu = StartPrepareCommand(INS.Sign);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
rqApdu.addTLV_U8(TLV.Tag.TAG_TrOut_HashSize, hashes[0].length);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, bs.toByteArray());
|
||||
byte[] hashesConcatenated = bs.toByteArray();
|
||||
rqApdu.addTLV(TLV.Tag.TAG_TrOut_Hash, hashesConcatenated);
|
||||
if (issuerData != null) {
|
||||
if (!mCard.allowedSigningMethod.contains(TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer_And_WriteIssuerData))
|
||||
throw new TangemException("Card don't support simultaneous sign with write issuer data!");
|
||||
|
|
@ -853,6 +870,8 @@ public class CardProtocol {
|
|||
throw new TangemException("Card require issuer validation before sign the transaction!");
|
||||
}
|
||||
|
||||
prepareDataForLinkingTerminal(rqApdu, hashesConcatenated);
|
||||
|
||||
Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" ")));
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
|
@ -915,6 +934,8 @@ public class CardProtocol {
|
|||
throw new TangemException("Card require issuer validation before sign the transaction!");
|
||||
}
|
||||
|
||||
prepareDataForLinkingTerminal(rqApdu, bTxOutData);
|
||||
|
||||
ResponseApdu rspApdu = SendAndReceive(rqApdu, false);
|
||||
|
||||
if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) {
|
||||
|
|
@ -935,6 +956,12 @@ public class CardProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareDataForLinkingTerminal(CommandApdu apdu, byte[] data) throws Exception {
|
||||
byte[] transactionSignature = CardCrypto.Signature(mCard.getTerminalPrivateKey(), data);
|
||||
apdu.addTLV(TLV.Tag.TAG_Terminal_TransactionSignature, transactionSignature);
|
||||
addTerminalPublicKeyToApdu(apdu);
|
||||
}
|
||||
|
||||
/**
|
||||
* VERIFY_CODE command
|
||||
* See [1] 8.8
|
||||
|
|
@ -952,7 +979,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII"));
|
||||
rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress);
|
||||
|
|
@ -988,7 +1016,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
private void run_ValidateCard(String PIN2) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2));
|
||||
|
||||
|
|
@ -1022,7 +1051,8 @@ public class CardProtocol {
|
|||
* @throws Exception - if something went wrong
|
||||
*/
|
||||
public void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception {
|
||||
if (readResult == null) throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
if (readResult == null)
|
||||
throw new TangemException("Before run_VerifyCard execute run_Read card first!");
|
||||
|
||||
CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData);
|
||||
rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ public class SettingsMask {
|
|||
|
||||
public static final int DisablePrecomputedNDEF = 0x00010000;
|
||||
|
||||
public static final int SkipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000;
|
||||
|
||||
public static String getDescription(int iValue) {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
sb.append("[");
|
||||
|
|
@ -58,6 +60,8 @@ public class SettingsMask {
|
|||
if ((iValue & SettingsMask.ForbidPurgeWallet) != 0) sb.append("ForbidPurgeWallet, ");
|
||||
if ((iValue & SettingsMask.AllowSelectBlockchain) != 0) sb.append("AllowSelectBlockchain, ");
|
||||
if ((iValue & SettingsMask.DisablePrecomputedNDEF) != 0) sb.append("DisablePrecomputedNDEF, ");
|
||||
if ((iValue & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0)
|
||||
sb.append("SkipSecurityDelayIfValidatedByLinkedTerminal, ");
|
||||
|
||||
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
|
||||
sb.append("]");
|
||||
|
|
|
|||
|
|
@ -85,8 +85,11 @@ public class TLV {
|
|||
TAG_Denomination(0xC0),
|
||||
TAG_ValidatedBalance(0xC1),
|
||||
TAG_LastSign_Date(0xC2),
|
||||
TAG_DenominationText(0xC3);
|
||||
TAG_DenominationText(0xC3),
|
||||
|
||||
TAG_Terminal_IsLinked(0x58),
|
||||
TAG_Terminal_PublicKey(0x5C),
|
||||
TAG_Terminal_TransactionSignature(0x57);
|
||||
|
||||
Tag(int Code) {
|
||||
this.Code = Code;
|
||||
|
|
|
|||
|
|
@ -235,6 +235,9 @@ public class CustomReadCardTask extends Thread {
|
|||
Log.e(TAG, "Can't get max signatures");
|
||||
}
|
||||
|
||||
TLV terminalIsLinked = protocol.getReadResult().getTLV(TLV.Tag.TAG_Terminal_IsLinked);
|
||||
mCard.setTerminalIsLinked(terminalIsLinked != null);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new CardProtocol.TangemException("Can't parse card data");
|
||||
|
|
@ -299,6 +302,9 @@ public class CustomReadCardTask extends Thread {
|
|||
if (isCancelled) return;
|
||||
|
||||
protocol.setPIN(CardProtocol.DefaultPIN);
|
||||
if (pinsProvider != null) {
|
||||
protocol.setTerminalPublicKey(pinsProvider.getTerminalPublicKey());
|
||||
}
|
||||
protocol.clearReadResult();
|
||||
|
||||
if (lastRead_Encryption == null) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public class SignTask extends CustomReadCardTask {
|
|||
mNotifications.onReadProgress(protocol, 30);
|
||||
if (isCancelled) return;
|
||||
|
||||
if (mCard.getPauseBeforePIN2() > 0) {
|
||||
if (mCard.getPauseBeforePIN2() > 0 && !mCard.getTerminalIsLinked()) {
|
||||
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import androidx.test.runner.AndroidJUnit4;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import javax.crypto.Cipher;
|
|||
|
||||
public class PINStorage implements PINsProvider {
|
||||
private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2;
|
||||
private static byte[] terminalPubicKey, terminalPrivateKey;
|
||||
private static SharedPreferences sharedPreferences = null;
|
||||
|
||||
public static void init(Context context) {
|
||||
|
|
@ -194,4 +195,19 @@ public class PINStorage implements PINsProvider {
|
|||
return sharedPreferences == null;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPublicKey() {
|
||||
return terminalPubicKey;
|
||||
}
|
||||
|
||||
public static void setTerminalPublicKey(byte[] terminalPubicKey) {
|
||||
PINStorage.terminalPubicKey = terminalPubicKey;
|
||||
}
|
||||
|
||||
public byte[] getTerminalPrivateKey() {
|
||||
return terminalPrivateKey;
|
||||
}
|
||||
|
||||
public static void setTerminalPrivateKey(byte[] terminalPrivateKey) {
|
||||
PINStorage.terminalPrivateKey = terminalPrivateKey;
|
||||
}
|
||||
}
|
||||
|
|
@ -81,6 +81,10 @@ fun TangemCard.loadFromBundle(B: Bundle) {
|
|||
if (B.containsKey("onlineValidated"))
|
||||
isOnlineValidated = B.getBoolean("onlineValidated")
|
||||
|
||||
if (B.containsKey("terminalPrivateKey")) terminalPrivateKey = B.getByteArray("terminalPrivateKey")
|
||||
if (B.containsKey("terminalPublicKey")) terminalPublicKey = B.getByteArray("terminalPublicKey")
|
||||
terminalIsLinked = B.getBoolean("terminalIsLinked")
|
||||
|
||||
}
|
||||
|
||||
val TangemCard.asBundle: Bundle
|
||||
|
|
@ -152,6 +156,13 @@ fun TangemCard.saveToBundle(B: Bundle) {
|
|||
|
||||
if (isOnlineValidated != null)
|
||||
B.putBoolean("onlineValidated", isOnlineValidated)
|
||||
|
||||
if (terminalPrivateKey != null)
|
||||
B.putByteArray("terminalPrivateKey", terminalPrivateKey)
|
||||
if (terminalPublicKey != null)
|
||||
B.putByteArray("terminalPublicKey", terminalPublicKey)
|
||||
B.putBoolean("terminalIsLinked", terminalIsLinked)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("Can't save to bundle ", e.message)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue