Updated on 2026-08-14

This commit is contained in:
Tangem 2018-11-14 12:14:31 +03:00
commit 2af8d5dc82
78 changed files with 646 additions and 575 deletions

View file

@ -46,7 +46,7 @@ public class LogFileProvider extends ContentProvider {
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
String LOG_TAG = CLASS_NAME+"-oF";
String LOG_TAG = CLASS_NAME + "-oF";
Log.v(LOG_TAG,
"Called with uri: '" + uri + "'." + uri.getLastPathSegment());

View file

@ -0,0 +1,285 @@
package com.tangem.data;
import android.content.Context;
import android.util.Log;
import com.tangem.util.Util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;
public class Logger {
public static File collectLogs(Context context) {
File f = new File(context.getCacheDir().getAbsolutePath() + "/Wallet_" + Util.formatDateTimeToFileName(new Date()) + ".log");
try {
if (f.createNewFile()) {
f.setReadable(true);
FileWriter fileWriter = new FileWriter(f, true);
Process process = Runtime.getRuntime().exec("logcat -d -b main -v time");
try {
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader bufferedReader = new BufferedReader(isr);
BufferedWriter buf = new BufferedWriter(fileWriter);
buf.append("Tangem Wallet logs");
buf.newLine();
int i = 0;
String line;
while ((line = bufferedReader.readLine()) != null) {
buf.append(line);
buf.newLine();
i++;
}
Log.e("Logger", String.format("%d log lines collected", i));
buf.newLine();
buf.flush();
buf.close();
} finally {
process.destroy();
}
return f;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//public class Logger {
//
// public static File[] getLastLogFiles() {
// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs");
// if (!path.exists()) {
// return null;
// }
// File[] files = path.listFiles();
// Arrays.sort(files, new Comparator<File>() {
// @Override
// public int compare(File o1, File o2) {
// if (o1.lastModified() < o2.lastModified()) {
// return -1;
// } else if (o1.lastModified() > o2.lastModified()) {
// return 1;
// }
// return 0;
// }
// });
// if (files.length < 5) return files;
// return Arrays.copyOfRange(files, files.length - 5, files.length);
// }
//
// private static File logFile = null;
//
// private static void initLogFile(Context context) {
// try {
// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs");
// if (!path.exists()) {
// path.mkdirs();
// MediaScannerConnection.scanFile(context, new String[]{path.getParentFile().toString()}, null, null);
// }
// logFile = new File(path, String.format("wallet_%s.log", Util.formatDateTimeToFileName(new Date())));
// logFile.createNewFile();
// logFile.setReadable(true);
//
// // initiate media scan and put the new things into the path array to
// // make the scanner aware of the location and the files you want to see
// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath()}, null, null);
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//
// public static boolean isCurrent(File f) {
// if (f == null || logFile == null) return false;
// return f.getAbsolutePath().equals(logFile.getAbsolutePath());
// }
//
//
// private static class LogCatThread extends Thread {
// private boolean Terminated;
//
// private static final Object oSync = new Object();
//
// public void Terminate() {
// Terminated = true;
// synchronized (oSync) {
// oSync.notifyAll();
// }
// try {
// join(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// interrupt();
// }
// }
//
// public void collectLogs(Writer out) {
// try {
// Process process = Runtime.getRuntime().exec("logcat -d -b main -v time");
// try {
// InputStream is = process.getInputStream();
// InputStreamReader isr = new InputStreamReader(is);
// BufferedReader bufferedReader = new BufferedReader(isr);
// try {
//
// try {
// //BufferedWriter for performance, true to set append to file flag
// BufferedWriter buf = new BufferedWriter(out);
//
// while (!Terminated && isr.ready()) {
// String line = bufferedReader.readLine();
// buf.append(line);
// buf.newLine();
// }
// //Log.i("Logger",String.format("%d lines added",i));
// buf.newLine();
// buf.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// } finally {
// process.destroy();
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//
// @Override
// public void run() {
// Process process = null;
// try {
// if (logFile == null) return;
// process = Runtime.getRuntime().exec("logcat -b main -v time");
// try {
// InputStream is = process.getInputStream();
// InputStreamReader isr = new InputStreamReader(is);
// BufferedReader bufferedReader = new BufferedReader(isr);
// while (!Terminated) {
// try {
// synchronized (oSync) {
// oSync.wait(1000);
// }
// if (!logFile.exists()) {
// try {
// logFile.createNewFile();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// try {
// //BufferedWriter for performance, true to set append to file flag
// BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
//
// while (isr.ready()) {
// String line = bufferedReader.readLine();
// buf.append(line);
// buf.newLine();
// }
// //Log.i("Logger",String.format("%d lines added",i));
// buf.newLine();
// buf.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// } finally {
// process.destroy();
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
//
// static LogCatThread t = new LogCatThread();
//
// public static void StartSaveToFile(Activity activity) {
// try {
// if (logFile != null) return;
//
// verifyStoragePermissions(activity);
// initLogFile(activity.getApplicationContext());
// t.start();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public static void StopSaveToFile(Context context) {
// final Object oSync = new Object();
// if (t.isAlive()) {
// t.Terminate();
// }
// if (logFile != null) {
// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath().toString()}, null, new MediaScannerConnection.OnScanCompletedListener() {
// @Override
// public void onScanCompleted(String path, Uri uri) {
//// synchronized (oSync) {
//// oSync.notifyAll();
//// }
// }
// });
//// try {
//// synchronized (oSync) {
//// oSync.wait(10000);
// logFile = null;
//// }
//// } catch (InterruptedException e) {
//// e.printStackTrace();
//// }
// }
//
// }
//
// // Storage Permissions
// private static final int REQUEST_EXTERNAL_STORAGE = 1;
// private static String[] PERMISSIONS_STORAGE = {
// Manifest.permission.READ_EXTERNAL_STORAGE,
// Manifest.permission.WRITE_EXTERNAL_STORAGE
// };
//
// //Checks if the app has permission to write to device storage
// //If the app does not has permission then the user will be prompted to grant permissions
// public static void verifyStoragePermissions(Activity activity) {
// // Check if we have write permission
// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
//
// if (permission != PackageManager.PERMISSION_GRANTED) {
// // We don't have permission so prompt the user
// ActivityCompat.requestPermissions(
// activity,
// PERMISSIONS_STORAGE,
// REQUEST_EXTERNAL_STORAGE
// );
// }
// }
//
//}

View file

@ -0,0 +1,295 @@
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

@ -0,0 +1,204 @@
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

@ -1,9 +1,8 @@
package com.tangem.data.network.task.save_pin;
package com.tangem.data.fingerprint;
import android.os.AsyncTask;
import android.widget.Toast;
import com.tangem.domain.wallet.FingerprintHelper;
import com.tangem.presentation.activity.PinSaveActivity;
import com.tangem.wallet.R;

View file

@ -0,0 +1,62 @@
package com.tangem.data.fingerprint;
import android.annotation.TargetApi;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
/**
* Created by dtaka on 8/20/2016.
*/
@TargetApi(Build.VERSION_CODES.M)
public class FingerprintHelper extends FingerprintManager.AuthenticationCallback {
private FingerprintHelperListener listener;
public FingerprintHelper(FingerprintHelperListener listener) {
this.listener = listener;
}
private CancellationSignal cancellationSignal;
public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
cancellationSignal = new CancellationSignal();
try {
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
} catch (SecurityException ex) {
listener.authenticationFailed("An error occurred:\n" + ex.getMessage());
} catch (Exception ex) {
listener.authenticationFailed("An error occurred\n" + ex.getMessage());
}
}
public void cancel() {
if (cancellationSignal != null)
cancellationSignal.cancel();
}
public interface FingerprintHelperListener {
public void authenticationFailed(String error);
public void authenticationSucceeded(FingerprintManager.AuthenticationResult result);
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
listener.authenticationFailed("Authentication error\n" + errString);
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
listener.authenticationFailed("Authentication help\n" + helpString);
}
@Override
public void onAuthenticationFailed() {
listener.authenticationFailed("Authentication failed.");
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
listener.authenticationSucceeded(result);
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.data.network.task.request_pin;
package com.tangem.data.fingerprint;
import android.annotation.TargetApi;
import android.hardware.fingerprint.FingerprintManager;
@ -8,8 +8,7 @@ import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import com.tangem.domain.wallet.FingerprintHelper;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.presentation.activity.PinRequestActivity;
import java.io.IOException;

View file

@ -37,9 +37,9 @@ import retrofit2.http.Path;
/**
* HTTP
* Used in Cryptonit_OtherAPI service
* Used in CryptonitOtherApi service
*/
public class Cryptonit_OtherAPI {
public class CryptonitOtherApi {
private static final String SERVER_URL = "https://api.cryptonit.net/api/";
private static class Method {
@ -115,7 +115,7 @@ public class Cryptonit_OtherAPI {
private ErrorListener errorListener;
private Context context;
public Cryptonit_OtherAPI(Context context) {
public CryptonitOtherApi(Context context) {
this.context = context;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
key = sp.getString(context.getResources().getString(R.string.key_cryptonit_key), "");

View file

@ -6,8 +6,6 @@ import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.CardVerifyAndGetInfo;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import com.tangem.data.network.model.RateInfoResponse;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;
@ -24,8 +22,8 @@ import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiHelper {
private static String TAG = ServerApiHelper.class.getSimpleName();
public class ServerApiCommon {
private static String TAG = ServerApiCommon.class.getSimpleName();
/**
* HTTP
@ -85,84 +83,6 @@ public class ServerApiHelper {
});
}
/**
* HTTP
* Infura
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String INFURA_ETH_GET_BALANCE = "eth_getBalance";
public static final String INFURA_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String INFURA_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String INFURA_ETH_CALL = "eth_call";
public static final String INFURA_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String INFURA_ETH_GAS_PRICE = "eth_gasPrice";
private InfuraBodyListener infuraBodyListener;
public interface InfuraBodyListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setInfuraResponse(InfuraBodyListener listener) {
infuraBodyListener = listener;
}
public void infura(String method, int id, String wallet, String contract, String tx) {
InfuraApi infuraApi = App.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class);
InfuraBody infuraBody;
switch (method) {
case INFURA_ETH_GET_BALANCE:
case INFURA_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case INFURA_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(INFURA_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case INFURA_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case INFURA_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case INFURA_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = infuraApi.infura(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
if (response.code() == 200) {
infuraBodyListener.onSuccess(method, response.body());
Log.i(TAG, "infura " + method + " onResponse " + response.code());
} else {
infuraBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "infura " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
infuraBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "infura " + method + " onFailure " + t.getMessage());
}
});
}
/**
* HTTP
* Card verify

View file

@ -3,9 +3,10 @@ package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.domain.BitcoinNode;
import com.tangem.domain.BitcoinNodeTestNet;
import com.tangem.domain.BitcoinCashNode;
import com.tangem.domain.wallet.btc.BitcoinNode;
import com.tangem.domain.wallet.btc.BitcoinNodeSsl;
import com.tangem.domain.wallet.btc.BitcoinNodeTestNet;
import com.tangem.domain.wallet.bch.BitcoinCashNode;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.domain.wallet.TangemCard;
@ -27,8 +28,6 @@ import java.util.Random;
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@ -38,9 +37,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
public class ServerApiHelperElectrum {
// private static String TAG = ServerApiHelper.class.getSimpleName();
private static String TAG = ServerApiHelperElectrum.class.getSimpleName();
public class ServerApiElectrum {
private static String TAG = ServerApiElectrum.class.getSimpleName();
/**
* TCP
@ -108,6 +106,11 @@ public class ServerApiHelperElectrum {
}
private List<ElectrumRequest> testSslSocket(TangemCard card, ElectrumRequest electrumRequest) {
BitcoinNodeSsl bitcoinNodeSsl = BitcoinNodeSsl.values()[new Random().nextInt(BitcoinNodeSsl.values().length)];
this.host = bitcoinNodeSsl.getHost();
this.port = bitcoinNodeSsl.getPort();
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket;
@ -115,8 +118,9 @@ public class ServerApiHelperElectrum {
Collections.addAll(result, electrumRequest);
try {
// socket = (SSLSocket) sf.createSocket("gmail.com", 443);
socket = (SSLSocket) sf.createSocket("electrum.hsmiths.com", 50002);
socket = (SSLSocket) sf.createSocket("gmail.com", 443);
// socket = (SSLSocket) sf.createSocket("electrum.hsmiths.com", 50002);
// socket = (SSLSocket) sf.createSocket(host, port);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
SSLSession s = socket.getSession();
@ -124,7 +128,7 @@ public class ServerApiHelperElectrum {
// throw new SSLHandshakeException("Expected mail.google.com, found " + s.getPeerPrincipal());
// }
Log.i(TAG, host + " " + port);
try {
OutputStream os = socket.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8");

View file

@ -0,0 +1,96 @@
package com.tangem.data.network;
import android.support.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiInfura {
private static String TAG = ServerApiInfura.class.getSimpleName();
/**
* HTTP
* Infura
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String INFURA_ETH_GET_BALANCE = "eth_getBalance";
public static final String INFURA_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String INFURA_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String INFURA_ETH_CALL = "eth_call";
public static final String INFURA_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String INFURA_ETH_GAS_PRICE = "eth_gasPrice";
private InfuraBodyListener infuraBodyListener;
public interface InfuraBodyListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setInfuraResponse(InfuraBodyListener listener) {
infuraBodyListener = listener;
}
public void infura(String method, int id, String wallet, String contract, String tx) {
InfuraApi infuraApi = App.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class);
InfuraBody infuraBody;
switch (method) {
case INFURA_ETH_GET_BALANCE:
case INFURA_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case INFURA_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(INFURA_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case INFURA_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case INFURA_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case INFURA_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = infuraApi.infura(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
if (response.code() == 200) {
infuraBodyListener.onSuccess(method, response.body());
Log.i(TAG, "infura " + method + " onResponse " + response.code());
} else {
infuraBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "infura " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
infuraBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "infura " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dmmatrix.epro.core.exception
package com.tangem.data.network.exception
/**
* Base Class for handling errors/failures/exceptions.

View file

@ -6,7 +6,7 @@ import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class CreateNewWalletTask extends Thread {

View file

@ -0,0 +1,40 @@
package com.tangem.data.nfc
import android.os.Build
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,4 +1,4 @@
package com.tangem.data
package com.tangem.data.nfc
enum class NFCLocation(val codename: String, val fullName: String, val orientation: Int, val x: Int, val y: Int, val z: Int) {
model1("sailfish", "Google Pixel", 0, 65, 25, 0),

View file

@ -6,7 +6,7 @@ import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class PurgeTask extends Thread {

View file

@ -6,7 +6,7 @@ import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.util.Util;

View file

@ -13,7 +13,7 @@ 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.util.BTCUtils;
import com.tangem.domain.wallet.BTCUtils;
import java.io.IOException;
@ -86,7 +86,7 @@ public class SignPaymentTask extends Thread {
// SignBTC_TX(protocol);
// }
CoinEngine engine = CoinEngineFactory.create(mCtx);
CoinEngine engine = CoinEngineFactory.INSTANCE.create(mCtx);
if (engine != null) {
if (mCtx.getCard().getPauseBeforePIN2() > 0) {
mNotifications.onReadWait(mCtx.getCard().getPauseBeforePIN2());

View file

@ -6,7 +6,7 @@ import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
public class SwapPINTask extends Thread {

View file

@ -7,7 +7,7 @@ import android.util.Log;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.FW;
import com.tangem.domain.cardReader.NfcManager;
import com.tangem.domain.wallet.PINStorage;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.wallet.TangemCard;
import java.util.Arrays;