Updated on 2026-08-14

This commit is contained in:
Tangem 2018-12-04 11:05:16 +03:00
parent ebc3abcfe1
commit 48db52b2a9
99 changed files with 860 additions and 895 deletions

View file

@ -68,8 +68,7 @@ public class LogFileProvider extends ContentProvider {
// Create & return a ParcelFileDescriptor pointing to the file
// Note: I don't care what mode they ask for - they're only getting
// read only
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(
fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri

View file

@ -3,7 +3,7 @@ package com.tangem.data;
import android.content.Context;
import android.util.Log;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import java.io.BufferedReader;
import java.io.BufferedWriter;

View file

@ -1,295 +0,0 @@
package com.tangem.data.db
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import java.io.File
import kotlin.collections.HashMap
import android.graphics.BitmapFactory
import android.util.Log
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import com.tangem.data.network.model.CardVerifyAndGetInfo
import com.tangem.domain.cardReader.CardCrypto
import com.tangem.domain.wallet.TangemCard
import com.tangem.util.Util
import com.tangem.wallet.R
import java.io.InputStream
import java.lang.Exception
import java.nio.charset.StandardCharsets
import java.util.*
data class LocalStorage(
val context: Context
) {
private lateinit var artworks: HashMap<String, ArtworkInfo>
private lateinit var batches: HashMap<String, BatchInfo>
private val artworksFile: File = File(context.filesDir, "artworks.json")
private val batchesFile: File = File(context.filesDir, "batches.json")
private var cacheDir: File? = null
init {
cacheDir = File(context.filesDir, "artworks")
if (!cacheDir!!.exists())
cacheDir!!.mkdirs()
if (artworksFile.exists()) {
try {
artworksFile.bufferedReader().use { artworks = Gson().fromJson(it, object : TypeToken<HashMap<String, ArtworkInfo>>() {}.type) }
} catch (e: Exception) {
e.printStackTrace()
artworks = HashMap()
}
} else {
artworks = HashMap()
}
if (artworks.count() == 0) {
putResourceArtworkToCatalog(R.drawable.card_default, false)
putResourceArtworkToCatalog(R.drawable.card_ru006, false)
putResourceArtworkToCatalog(R.drawable.card_ru007, false)
putResourceArtworkToCatalog(R.drawable.card_ru011, false)
putResourceArtworkToCatalog(R.drawable.card_ru012, false)
putResourceArtworkToCatalog(R.drawable.card_ru013, false)
putResourceArtworkToCatalog(R.drawable.card_ru014, false)
putResourceArtworkToCatalog(R.drawable.card_ru015, false)
putResourceArtworkToCatalog(R.drawable.card_ru016, false)
putResourceArtworkToCatalog(R.drawable.card_ru020, false)
putResourceArtworkToCatalog(R.drawable.card_ru021, false)
putResourceArtworkToCatalog(R.drawable.card_ru022, false)
putResourceArtworkToCatalog(R.drawable.card_ru023, true)
putResourceArtworkToCatalog(R.drawable.card_ru024, true)
putResourceArtworkToCatalog(R.drawable.card_ru028, true)
putResourceArtworkToCatalog(R.drawable.card_ru029, true)
putResourceArtworkToCatalog(R.drawable.card_ru030, true)
}
if (batchesFile.exists()) {
try {
batchesFile.bufferedReader().use { batches = Gson().fromJson(it, object : TypeToken<HashMap<String, BatchInfo>>() {}.type) }
} catch (e: Exception) {
e.printStackTrace()
batches = HashMap()
}
} else {
batches = HashMap()
}
}
private fun getArtworkFile(artworkId: String): File {
return File(cacheDir, "$artworkId.png")
}
private fun loadArtworkBitmapFromFile(artworkId: String): Bitmap? {
return try {
BitmapFactory.decodeFile(getArtworkFile(artworkId).absolutePath)
} catch (E: Exception) {
E.printStackTrace()
null
}
}
private fun saveArtworkBitmapToFile(artworkId: String, data: ByteArray) {
val file = getArtworkFile(artworkId)
file.writeBytes(data)
}
fun checkNeedUpdateArtwork(artwork: CardVerifyAndGetInfo.Response.Item.ArtworkInfo?): Boolean {
if (artwork == null || artwork.id.isBlank()) return false
val localArtwork = artworks[artwork.id] ?: return true
if (localArtwork.hash == artwork.hash) return false
return localArtwork.updateDate == null || localArtwork.updateDate.before(artwork.getUpdateDate())
}
fun checkBatchInfoChanged(card: TangemCard, result: CardVerifyAndGetInfo.Response.Item): Boolean {
var sData: String? = result.substitution?.data
var sSignature: String? = result.substitution?.signature
if (card.batch != result.batch) {
Log.e("LocalStorage", "Invalid batch received!")
return false
}
if (!BatchInfo.CardDataSubstitution.verifySignature(card, sData, sSignature)) {
sData = null
sSignature = null
}
val batchInfo = batches[result.batch]
if (batchInfo == null || batchInfo.artworkId?.toLowerCase() != result.artwork?.id?.toLowerCase() || batchInfo.dataSubstitution != sData) {
putBatchToCatalog(result.batch, result.artwork?.id?.toLowerCase(), sData, sSignature)
return true
}
return false
}
fun updateArtwork(artworkId: String, inputStream: InputStream, updateDate: Date) {
val data = inputStream.readBytes()
saveArtworkBitmapToFile(artworkId.toLowerCase(), data)
putArtworkToCatalog(artworkId, false, data, updateDate, true)
}
// private fun putBatchToCatalog(batch: String, resourceId: Int, forceSave: Boolean = true) {
// putBatchToCatalog(batch, context.resources.getResourceEntryName(resourceId), null, null, forceSave)
// }
private fun putBatchToCatalog(batch: String, artworkId: String?, substitution: String?, substitutionSignature: String?, forceSave: Boolean = true) {
batches[batch] = BatchInfo(artworkId, substitution, substitutionSignature)
if (forceSave) {
val sBatches = Gson().toJson(batches)
batchesFile.bufferedWriter().use { it.write(sBatches) }
}
}
@SuppressLint("ResourceType")
private fun putResourceArtworkToCatalog(resourceId: Int, forceSave: Boolean) {
context.resources.openRawResource(R.drawable.card_default).use { putArtworkToCatalog(context.resources.getResourceEntryName(resourceId), true, it.readBytes(), null, forceSave) }
}
private fun putArtworkToCatalog(artworkId: String, isResource: Boolean, data: ByteArray, updateDate: Date?, forceSave: Boolean = true) {
val artworkInfo = ArtworkInfo(
isResource, Util.bytesToHex(Util.calculateSHA256(data)), updateDate
)
artworks[artworkId.toLowerCase()] = artworkInfo
if (forceSave) {
val sArtworks = Gson().toJson(artworks)
artworksFile.bufferedWriter().use { it.write(sArtworks) }
}
}
private fun getArtworkBitmap(artworkId: String): Bitmap? {
if (artworkId.isBlank()) return null
val info = artworks[artworkId.toLowerCase()] ?: return null
return if (info.isResource) {
val resID = context.resources.getIdentifier(artworkId, "drawable", context.packageName)
if (resID == 0) return null
BitmapFactory.decodeResource(context.resources, resID)
} else {
loadArtworkBitmapFromFile(artworkId)
}
}
private fun getDefaultArtworkBitmap(): Bitmap {
val defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default)
val info = artworks[defaultArtworkId]
var bitmap: Bitmap? = null
if (info != null && !info.isResource) {
bitmap = loadArtworkBitmapFromFile(defaultArtworkId)
}
if (bitmap == null) return BitmapFactory.decodeResource(context.resources, R.drawable.card_default)
return bitmap
}
fun getCardArtworkBitmap(card: TangemCard): Bitmap {
// special cases (first series of cards, hardcode CID->artwork), on new series batch<->artwork
val hexCID = Util.bytesToHex(card.cid)
val artworkResourceId: Int? = when {
hexCID in "AA01000000000000".."AA01000000004999" -> R.drawable.card_ru006
hexCID in "AA01000000005000".."AA01000000009999" -> R.drawable.card_ru007
hexCID in "AE01000000000000".."AE01000000004999" -> R.drawable.card_ru006
hexCID in "AE01000000005000".."AE01000000009999" -> R.drawable.card_ru007
hexCID in "CB01000000000000".."CB01000000009999" -> R.drawable.card_ru006
hexCID in "CB01000000010000".."CB01000000019999" -> R.drawable.card_ru007
hexCID in "CB01000000020000".."CB01000000039999" -> R.drawable.card_ru006
hexCID in "CB01000000040000".."CB01000000059999" -> R.drawable.card_ru007
hexCID in "CB02000000000000".."CB02000000024999" -> R.drawable.card_ru006
hexCID in "CB02000000025000".."CB02000000049999" -> R.drawable.card_ru007
hexCID in "CB05000010000000".."CB05000010009999" -> R.drawable.card_ru006
card.batch == "0004" -> R.drawable.card_ru006
card.batch == "0006" -> R.drawable.card_ru006
card.batch == "0010" -> R.drawable.card_ru006
card.batch == "0005" -> R.drawable.card_ru007
card.batch == "0007" -> R.drawable.card_ru007
card.batch == "0011" -> R.drawable.card_ru007
card.batch == "0012" -> R.drawable.card_ru011
card.batch == "0013" -> R.drawable.card_ru012
card.batch == "0014" -> R.drawable.card_ru006
card.batch == "0015" -> R.drawable.card_ru020
card.batch == "0016" -> R.drawable.card_ru021
card.batch == "0017" -> R.drawable.card_ru013
card.batch == "0019" -> R.drawable.card_ru016
card.batch == "001A" -> R.drawable.card_ru014
card.batch == "001B" -> R.drawable.card_ru015
card.batch == "001C" -> R.drawable.card_ru023
card.batch == "001D" -> R.drawable.card_ru022
card.batch == "001E" -> R.drawable.card_ru024
card.batch == "001F" -> R.drawable.card_ru028
card.batch == "0018" -> R.drawable.card_ru029
card.batch == "0020" -> R.drawable.card_ru030
else -> null
}
if (artworkResourceId != null) {
val artworkId = context.resources.getResourceEntryName(artworkResourceId)
return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
}
val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap()
val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap()
return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
}
fun applySubstitution(card: TangemCard) {
val batchInfo = batches[card.batch] ?: return
val substitution = batchInfo.getDataSubstitution(card) ?: return
substitution.applyToCard(card)
}
private data class ArtworkInfo(
val isResource: Boolean,
val hash: String,
val updateDate: Date?
)
private data class BatchInfo(
val artworkId: String?,
val dataSubstitution: String?,
val dataSubstitutionSignature: String?
) {
fun getDataSubstitution(card: TangemCard): CardDataSubstitution? {
return if (CardDataSubstitution.verifySignature(card, dataSubstitution, dataSubstitutionSignature)) {
Gson().fromJson(dataSubstitution, CardDataSubstitution::class.java)
} else {
null
}
}
class CardDataSubstitution(
@SerializedName("token_symbol")
private val tokenSymbol: String?,
@SerializedName("token_decimal")
private val tokenDecimal: Int?,
@SerializedName("token_contract_address")
private val contractAddress: String?
) {
fun applyToCard(card: TangemCard) {
if (tokenSymbol != null && (card.tokenSymbol.isNullOrBlank() || card.tokenSymbol.toLowerCase() == "not defined")) card.tokenSymbol = tokenSymbol
if (tokenDecimal != null && card.tokensDecimal == 0) card.tokensDecimal = tokenDecimal
if (contractAddress != null && (card.contractAddress.isNullOrBlank() || card.contractAddress.toLowerCase() == "not defined")) card.contractAddress = contractAddress
}
companion object {
fun verifySignature(card: TangemCard, substitutionData: String?, substitutionSignature: String?): Boolean {
if (substitutionData == null && substitutionSignature == null) return true
if (substitutionData == null || substitutionSignature == null) return false
val dataToSign = card.batch.toByteArray(StandardCharsets.UTF_8) + substitutionData.toByteArray(StandardCharsets.UTF_8)
return try {
CardCrypto.VerifySignature(card.issuerPublicDataKey, dataToSign, Util.hexToBytes(substitutionSignature))
} catch (E: Exception) {
E.printStackTrace()
false
}
}
}
}
}
}

View file

@ -1,204 +0,0 @@
package com.tangem.data.db;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import com.tangem.domain.cardReader.CardProtocol;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
/**
* Created by dvol on 12.09.2017.
* Global PIN Storage
*/
public class PINStorage {
private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2;
private static SharedPreferences sharedPreferences = null;
public static void init(Context context) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
mSavedPIN = sharedPreferences.getString("SavedPIN", null);
mUserPIN = null;
mLastUsedPIN = null;
mEncryptedPIN = null;
mPIN2 = null;
}
public static List<String> getPINs() {
ArrayList<String> result = new ArrayList<>();
if (mLastUsedPIN != null) result.add(mLastUsedPIN);
if (mEncryptedPIN != null && !result.contains(mEncryptedPIN)) result.add(mEncryptedPIN);
if (mUserPIN != null && !result.contains(mUserPIN)) result.add(mUserPIN);
if (mSavedPIN != null && !result.contains(mSavedPIN)) result.add(mSavedPIN);
if (!result.contains(CardProtocol.DefaultPIN)) result.add(CardProtocol.DefaultPIN);
return result;
}
public static void setLastUsedPIN(String PIN) {
mLastUsedPIN = PIN;
}
public static void setUserPIN(String PIN) {
mUserPIN = PIN;
}
public static void setPIN2(String PIN) {
mPIN2 = PIN;
}
public static void savePIN(String PIN) {
mSavedPIN = PIN;
if (mSavedPIN != null && !mSavedPIN.isEmpty()) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("SavedPIN", mSavedPIN);
editor.apply();
} else {
deletePIN();
}
}
public static void deletePIN() {
SharedPreferences.Editor editor = sharedPreferences.edit();
if (mSavedPIN != null && mSavedPIN.equals(mLastUsedPIN)) {
mLastUsedPIN = null;
}
mSavedPIN = null;
editor.remove("SavedPIN");
editor.apply();
}
public static void saveEncryptedPIN(Cipher cipher, String PIN) {
try {
byte[] iv = cipher.getIV();
byte[] bytes = cipher.doFinal(PIN.getBytes());
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("EncryptedPIN", encryptedPIN);
editor.putString("EncryptedIV", sIV);
editor.apply();
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] loadEncryptedIV() {
String sIV = sharedPreferences.getString("EncryptedIV", "");
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
return Base64.decode(sIV, Base64.NO_WRAP);
}
public static String loadEncryptedPIN(Cipher cipher) {
String encryptedPIN = sharedPreferences.getString("EncryptedPIN", null);
try {
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
mEncryptedPIN = new String(cipher.doFinal(bytes));
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
} catch (Exception e) {
e.printStackTrace();
mEncryptedPIN = null;
}
return mEncryptedPIN;
}
public static boolean haveEncryptedPIN() {
return sharedPreferences.getString("EncryptedPIN", null) != null;
}
public static void deleteEncryptedPIN() {
if (mEncryptedPIN != null && mEncryptedPIN.equals(mLastUsedPIN)) {
mLastUsedPIN = null;
}
mEncryptedPIN = null;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove("EncryptedPIN");
editor.remove("EncryptedIV");
editor.apply();
}
public static void saveEncryptedPIN2(Cipher cipher, String PIN) {
try {
byte[] iv = cipher.getIV();
byte[] bytes = cipher.doFinal(PIN.getBytes());
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("EncryptedPIN2", encryptedPIN);
editor.putString("EncryptedIV2", sIV);
editor.apply();
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] loadEncryptedIV2() {
String sIV = sharedPreferences.getString("EncryptedIV2", "");
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
return Base64.decode(sIV, Base64.NO_WRAP);
}
public static String loadEncryptedPIN2(Cipher cipher) {
String encryptedPIN = sharedPreferences.getString("EncryptedPIN2", null);
try {
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
mPIN2 = new String(cipher.doFinal(bytes));
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
} catch (Exception e) {
e.printStackTrace();
mPIN2 = null;
}
return mPIN2;
}
public static boolean haveEncryptedPIN2() {
return sharedPreferences.getString("EncryptedPIN2", null) != null;
}
public static void deleteEncryptedPIN2() {
mPIN2 = null;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove("EncryptedPIN2");
editor.remove("EncryptedIV2");
editor.apply();
}
public static String getPIN2() {
return mPIN2;
}
public static boolean isDefaultPIN(String pin) {
return (CardProtocol.DefaultPIN.equals(pin));
}
public static boolean isDefaultPIN2(String pin2) {
return (CardProtocol.DefaultPIN2.equals(pin2));
}
public static String getDefaultPIN() {
return CardProtocol.DefaultPIN;
}
public static String getDefaultPIN2() {
return CardProtocol.DefaultPIN2;
}
public static boolean needInit() {
return sharedPreferences == null;
}
}

View file

@ -8,7 +8,7 @@ import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import com.tangem.data.db.PINStorage;
import com.tangem.tangemcard.data.PINStorage;
import com.tangem.presentation.activity.PinRequestActivity;
import java.io.IOException;

View file

@ -4,30 +4,22 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.util.Util;
import com.tangem.wallet.R;
import java.io.IOException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
//import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
@ -36,9 +28,7 @@ import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
/**
* HTTP

View file

@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@ -12,7 +11,7 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.LinkedTreeMap;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import com.tangem.wallet.R;
import java.io.IOException;

View file

@ -4,10 +4,9 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import com.tangem.wallet.R;
import org.spongycastle.util.encoders.Base64;

View file

@ -7,8 +7,8 @@ import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.CardVerifyAndGetInfo;
import com.tangem.data.network.model.RateInfoResponse;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.tangemcard.util.Util;
import java.io.InputStream;
import java.util.ArrayList;

View file

@ -3,8 +3,8 @@ package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.bch.BitcoinCashNode;
import com.tangem.domain.wallet.btc.BitcoinNode;
import com.tangem.domain.wallet.btc.BitcoinNodeTestNet;

View file

@ -1,100 +0,0 @@
package com.tangem.data.nfc;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class CreateNewWalletTask extends Thread {
public static final String TAG = CreateNewWalletTask.class.getSimpleName();
private Context mContext;
private TangemCard mCard;
private NfcManager mNfcManager;
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public CreateNewWalletTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
mCard = card;
mContext = context;
mNfcManager = nfcManager;
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
mNotifications.onReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start create new wallet --]");
if (isCancelled) return;
protocol.run_VerifyCard();
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
mNotifications.onReadProgress(protocol, 30);
if (isCancelled) return;
// if (mCard.getPauseBeforePIN2() > 0) {
// mNotifications.onReadWait(mCard.getPauseBeforePIN2());
// }
// try {
protocol.run_CreateWallet(PINStorage.getPIN2());
// } finally {
// mNotifications.onReadWait(0);
// }
mNotifications.onReadProgress(protocol, 60);
if (isCancelled) return;
protocol.run_Read();
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish create new wallet --]");
mNotifications.onReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,41 +0,0 @@
package com.tangem.data.nfc
import android.os.Build
import com.tangem.tangemcard.nfc.NFCLocation
class DeviceNFCAntennaLocation {
companion object {
const val CARD_ON_BACK = 0
const val CARD_ON_FRONT = 1
const val CARD_ORIENTATION_HORIZONTAL = 0
const val CARD_ORIENTATION_VERTICAL = 1
}
var orientation: Int = 0
var fullName: String = ""
var x: Float = 0.toFloat()
var y: Float = 0.toFloat()
var z: Int = 0
fun getAntennaLocation() {
val codename = Build.DEVICE
// default values
this.orientation = 0
this.fullName = ""
this.x = 0.5f
this.y = 0.35f
this.z = 0
for (nfcLocation in NFCLocation.values()) {
if (codename.startsWith(nfcLocation.codename)) {
this.fullName = nfcLocation.fullName
this.orientation = nfcLocation.orientation
this.x = nfcLocation.x / 100f
this.y = nfcLocation.y / 100f
this.z = nfcLocation.z
}
}
}
}

View file

@ -1,102 +0,0 @@
package com.tangem.data.nfc;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class PurgeTask extends Thread {
public static final String TAG = PurgeTask.class.getSimpleName();
private String txOutAddress;
private Context mContext;
private TangemCard mCard;
private NfcManager mNfcManager;
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public PurgeTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
mCard = card;
mContext = context;
mNfcManager = nfcManager;
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
mNotifications.onReadStart(protocol);
try {
// for Samsung's bugs - Workaround for the Samsung Galaxy S5 (since the first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start purge --]");
if (isCancelled) return;
if (mCard.getPauseBeforePIN2() > 0) {
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
}
// try {
protocol.run_PurgeWallet(PINStorage.getPIN2());
// } finally {
// mNotifications.onReadWait(0);
// }
mNotifications.onReadProgress(protocol, 50);
protocol.run_Read();
mNotifications.onReadProgress(protocol, 100);
if (isCancelled)
return;
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
mNotifications.onReadWait(0);
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish purge --]");
mNotifications.onReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,173 +0,0 @@
package com.tangem.data.nfc;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;
import java.util.ArrayList;
public class ReadCardInfoTask extends Thread {
public static final String TAG = ReadCardInfoTask.class.getSimpleName();
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
private Context mContext;
private NfcManager mNfcManager;
// this fields are static to optimize process when need enter pin and scan card again
private static ArrayList<String> lastRead_UnsuccessfullPINs = new ArrayList<>();
private static TangemCard.EncryptionMode lastRead_Encryption = null;
private static String lastRead_UID;
public static void resetLastReadInfo() {
lastRead_UID="";
lastRead_Encryption=null;
lastRead_UnsuccessfullPINs.clear();
}
public ReadCardInfoTask(Context context, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
mContext = context;
mIsoDep = isoDep;
mNotifications = notifications;
mNfcManager = nfcManager;
// ReadCardInfoTask.lastRead_UID = lastRead_UID;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mNotifications);
mNotifications.onReadStart(protocol);
try {
mNotifications.onReadProgress(protocol, 5);
byte[] UID = mIsoDep.getTag().getId();
String sUID = Util.byteArrayToHexString(UID);
if (!lastRead_UID.equals(sUID)) {
resetLastReadInfo();
}
Log.i(TAG, "[-- Start read card info --]");
if (isCancelled) return;
protocol.setPIN(PINStorage.getDefaultPIN());
protocol.clearReadResult();
if (lastRead_Encryption == null) {
Log.i(TAG, "Try get supported encryption mode");
protocol.run_GetSupportedEncryption();
} else {
Log.i(TAG, "Use already defined encryption mode: " + lastRead_Encryption.name());
protocol.getCard().encryptionMode = lastRead_Encryption;
}
if (protocol.haveReadResult()) {
//already have read result (obtained while get supported encryption), only read issuer data and define offline balance
protocol.parseReadResult();
protocol.run_ReadWriteIssuerData();
mNotifications.onReadProgress(protocol, 60);
PINStorage.setLastUsedPIN(protocol.getCard().getPIN());
} else {
//don't have read result - may be don't get supported encryption on this try, need encryption or need another PIN
if (lastRead_Encryption == null) {
// we try get supported encryption on this time
lastRead_Encryption = protocol.getCard().encryptionMode;
if (protocol.getCard().encryptionMode == TangemCard.EncryptionMode.None) {
// default pin not accepted
lastRead_UnsuccessfullPINs.add(PINStorage.getDefaultPIN());
}
}
boolean pinFound = false;
for (String PIN : PINStorage.getPINs()) {
Log.e(TAG, "PIN: " + PIN);
boolean skipPin = false;
for (int i = 0; i < lastRead_UnsuccessfullPINs.size(); i++) {
if (lastRead_UnsuccessfullPINs.get(i).equals(PIN)) {
skipPin = true;
break;
}
}
if (skipPin) {
Log.e(TAG, "Skip PIN - already checked before");
continue;
}
try {
protocol.setPIN(PIN);
if (protocol.getCard().encryptionMode != TangemCard.EncryptionMode.None) {
protocol.CreateProtocolKey();
}
protocol.run_Read();
mNotifications.onReadProgress(protocol, 60);
PINStorage.setLastUsedPIN(PIN);
pinFound = true;
protocol.getCard().setPIN(PIN);
break;
} catch (CardProtocol.TangemException_InvalidPIN e) {
Log.e(TAG, e.getMessage());
lastRead_UnsuccessfullPINs.add(PIN);
}
}
if (!pinFound) {
throw new CardProtocol.TangemException_InvalidPIN("No valid PIN found!");
}
}
protocol.run_CheckPIN2isDefault();
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish read card info --]");
mNotifications.onReadFinish(protocol);
}
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
mNfcManager.notifyReadResult(false);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,159 +0,0 @@
package com.tangem.data.nfc;
import android.app.Activity;
import android.content.Intent;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.CoinEngineFactory;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.presentation.activity.SendTransactionActivity;
import com.tangem.presentation.activity.SignPaymentActivity;
import com.tangem.domain.wallet.BTCUtils;
import java.io.IOException;
public class SignPaymentTask extends Thread {
public static final String TAG = SignPaymentTask.class.getSimpleName();
private CoinEngine.Amount txAmount;
private CoinEngine.Amount txFee;
private Boolean txIncFee = true;
public void SetTransactionValue(CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean incfee) {
txAmount = amount;
txFee = fee;
txIncFee = incfee;
}
private String txOutAddress;
private Activity mContext;
private TangemContext mCtx;
private NfcManager mNfcManager;
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public SignPaymentTask(Activity context, TangemContext ctx, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications, CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean IncFee, String outAddress) {
mCtx=ctx;
mContext = context;
mNfcManager = nfcManager;
mIsoDep = isoDep;
mNotifications = notifications;
txOutAddress = outAddress;
SetTransactionValue(amount, fee, IncFee);
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCtx.getCard(), mNotifications);
mNotifications.onReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start sign payment --]");
if (isCancelled) return;
protocol.run_Read(false);
protocol.run_VerifyCard();
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
mNotifications.onReadProgress(protocol, 30);
if (isCancelled) return;
//
// if (mCard.getBlockchain() == Blockchain.Ethereum) {
// SignETH_TX(protocol);
// } else {
// SignBTC_TX(protocol);
// }
CoinEngine engine = CoinEngineFactory.INSTANCE.create(mCtx);
if (engine != null) {
if (mCtx.getCard().getPauseBeforePIN2() > 0) {
mNotifications.onReadWait(mCtx.getCard().getPauseBeforePIN2());
}
byte[] tx = null;
try {
tx = engine.sign(txFee, txAmount, txIncFee, txOutAddress, protocol);
}
catch (IOException e) {
e.printStackTrace();
protocol.setError(e);
} finally {
mNotifications.onReadWait(0);
}
if (tx != null) {
// TODO - move to engine!!!
String txStr = BTCUtils.toHex(tx);
if (mCtx.getBlockchain() == Blockchain.Ethereum || mCtx.getBlockchain() == Blockchain.EthereumTestNet || mCtx.getBlockchain() == Blockchain.Token) {
txStr = String.format("0x%s", txStr);
}
Intent intent = new Intent(mContext, SendTransactionActivity.class);
mCtx.saveToIntent(intent);
intent.putExtra(SendTransactionActivity.EXTRA_TX, txStr);
mContext.startActivityForResult(intent, SignPaymentActivity.REQUEST_CODE_SEND_PAYMENT);
}
}
mNotifications.onReadProgress(protocol, 100);
if (isCancelled) return;
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
mNotifications.onReadWait(0);
}
} catch (CardProtocol.TangemException_InvalidPIN e) {
e.printStackTrace();
protocol.setError(e);
} catch (CardProtocol.TangemException_WrongAmount e) {
e.printStackTrace();
protocol.setError(e);
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish sign payment --]");
mNotifications.onReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,106 +0,0 @@
package com.tangem.data.nfc;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class SwapPINTask extends Thread {
public static final String TAG = SwapPINTask.class.getSimpleName();
private Context mContext;
private TangemCard mCard;
private NfcManager mNfcManager;
private String newPIN, newPIN2;
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public SwapPINTask(Context context, TangemCard card, NfcManager nfcManager, String newPIN, String newPIN2, IsoDep isoDep, CardProtocol.Notifications notifications) {
this.newPIN = newPIN;
this.newPIN2 = newPIN2;
mCard = card;
mContext = context;
mNfcManager = nfcManager;
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
mNotifications.onReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start swap pin --]");
if (isCancelled) return;
if (mCard.getPauseBeforePIN2() > 0) {
mNotifications.onReadWait(mCard.getPauseBeforePIN2());
}
// try {
protocol.run_SetPIN(PINStorage.getPIN2(), newPIN, newPIN2, false);
protocol.setPIN(newPIN);
mCard.setPIN(newPIN);
// } finally {
// mNotifications.onReadWait(0);
// }
mNotifications.onReadProgress(protocol, 50);
protocol.run_Read();
mNotifications.onReadProgress(protocol, 100);
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
mNotifications.onReadWait(0);
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish purge --]");
mNotifications.onReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -1,116 +0,0 @@
package com.tangem.data.nfc;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.Firmwares;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
import java.util.Arrays;
/**
* Created by dvol on 04.02.2018.
*/
public class VerifyCardTask extends Thread {
public static final String TAG = VerifyCardTask.class.getSimpleName();
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
private Context mContext;
private TangemCard mCard;
private NfcManager mNfcManager;
public VerifyCardTask(Context context, TangemCard card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) {
mCard = card;
mContext = context;
mIsoDep = isoDep;
mNotifications = notifications;
mNfcManager = nfcManager;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications);
mNotifications.onReadStart(protocol);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start verify card --]");
if (isCancelled) return;
String PIN = mCard.getPIN();
protocol.setPIN(PIN);
protocol.run_Read(false);
PINStorage.setLastUsedPIN(PIN);
mNotifications.onReadProgress(protocol, 20);
if (isCancelled) return;
protocol.run_VerifyCard();
mNotifications.onReadProgress(protocol, 50);
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
if (isCancelled) return;
if (protocol.getCard().getStatus() == TangemCard.Status.Loaded) {
protocol.run_CheckWalletWithSignatureVerify();
mNotifications.onReadProgress(protocol, 80);
}
if (isCancelled) return;
Firmwares.VerifyCodeRecord record=Firmwares.selectRandomVerifyCodeBlock(mCard.getFirmwareVersion());
if (isCancelled) return;
if( record!=null ) {
byte[] returnedDigest = protocol.run_VerifyCode(record.hashAlg, record.blockIndex, record.blockCount, record.challenge);
mCard.setCodeConfirmed(Arrays.equals(returnedDigest, record.digest));
}else{
mCard.setCodeConfirmed(null);
}
mNotifications.onReadProgress(protocol, 90);
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish verify card --]");
mNotifications.onReadFinish(protocol);
}
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}