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();
}
}
}

View file

@ -4,7 +4,7 @@ import android.app.Activity
import android.nfc.Tag
import android.os.Bundle
import com.tangem.domain.wallet.CoinData
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.data.TangemCard
import com.tangem.presentation.activity.EmptyWalletActivity
import com.tangem.presentation.activity.LoadedWalletActivity
import com.tangem.presentation.activity.MainActivity

View file

@ -1,177 +0,0 @@
package com.tangem.domain.cardReader;
import android.util.Log;
import com.tangem.util.Util;
import org.spongycastle.asn1.ASN1EncodableVector;
import org.spongycastle.asn1.ASN1Integer;
import org.spongycastle.asn1.DERSequence;
import org.spongycastle.jce.ECNamedCurveTable;
import org.spongycastle.jce.spec.ECNamedCurveParameterSpec;
import org.spongycastle.jce.spec.ECPrivateKeySpec;
import org.spongycastle.jce.spec.ECPublicKeySpec;
import org.spongycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by dvol on 14.11.2017.
*/
public class CardCrypto {
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception {
if( publicKeyArray==null ) throw new Exception("Public key not specified!");
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyFactory factory = KeyFactory.getInstance("EC", "SC");
ECPoint p1 = spec.getCurve().decodePoint(publicKeyArray);
ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec);
return factory.generatePublic(keySpec);
}
public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception {
Signature signatureInstance = Signature.getInstance("SHA256withECDSA");
PublicKey publicKey = LoadPublicKey(publicKeyArray);
signatureInstance.initVerify(publicKey);
signatureInstance.update(data);
ASN1EncodableVector v = new ASN1EncodableVector();
int size = signature.length / 2;
v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, 0, size))));
v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, size, size * 2))));
byte[] sigDer = new DERSequence(v).getEncoded();
return signatureInstance.verify(sigDer);
}
public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyFactory factory = KeyFactory.getInstance("EC", "SC");
ECPrivateKeySpec keySpecP = new ECPrivateKeySpec(new BigInteger(1,privateKeyArray), spec);
Signature signature = Signature.getInstance("SHA256withECDSA");
PrivateKey privateKey = factory.generatePrivate(keySpecP);
signature.initSign(privateKey);
signature.update(data);
byte[] enc = signature.sign();
if (enc[0] != 0x30) throw new Exception("bad encoding 1");
if ((enc[1] & 0x80) != 0) throw new Exception("unsupported length encoding 1");
if (enc[2] != 0x02) throw new Exception("bad encoding 2");
if ((enc[3] & 0x80) != 0) throw new Exception("unsupported length encoding 2");
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");
int sLength = enc[5 + rLength];
int sPos = 6 + rLength;
byte[] res = new byte[64];
if (rLength <= 32) {
System.arraycopy(enc, 4, res, 32-rLength, rLength);
rLength=32;
} else if (rLength == 33 && enc[4] == 0) {
rLength--;
System.arraycopy(enc, 5, res, 0, rLength);
} else {
Log.e("cardCrypto","r-length:" + String.valueOf(rLength));
Log.e("cardCrypto","s-length:" + String.valueOf(sLength));
Log.e("cardCrypto","enc:" + Util.bytesToHex(enc));
throw new Exception("unsupported r-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc));
}
if (sLength <= 32) {
System.arraycopy(enc, sPos, res, rLength+32-sLength, sLength);
sLength=32;
} else if (sLength == 33 && enc[sPos] == 0) {
System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1);
} else {
Log.e("cardCrypto","s-length:" + String.valueOf(sLength));
Log.e("cardCrypto","r-length:" + String.valueOf(rLength));
Log.e("cardCrypto","enc:" +Util.bytesToHex(enc));
throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc));
}
if(!VerifySignature(GeneratePublicKey(privateKeyArray), data, res))
{
throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)+",res:"+Util.bytesToHex(res));
}
return res;
}
public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws NoSuchProviderException, NoSuchAlgorithmException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1,privateKeyArray)).getEncoded(false);
return publicKeyArray;
}
/**
* Computes the PBKDF2 hash of a password.
*
* @param password the password to hash.
* @param salt the salt
* @param iterations the iteration count (slowness factor)
* @return the PBDKF2 hash of the password
*/
public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations)
throws InvalidKeyException {
return PBKDF2.deriveKey(password, salt, iterations);
}
public static byte[] Encrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] mEncryptedData = cipher.doFinal(data);
return mEncryptedData;
}
public static byte[] Decrypt(byte[] key, byte[] data, boolean UsePKCS7)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
if (UsePKCS7) {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
} else {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length));
return decryptedData;
}
}
}

View file

@ -1,276 +0,0 @@
package com.tangem.domain.cardReader;
import com.tangem.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
public class CommandApdu {
public static final byte ISO_CLA = (byte) 0x00;
protected String mCmdName;
protected int mCla = 0x00;
protected int mIns = 0x00;
protected int mP1 = 0x00;
protected int mP2 = 0x00;
protected int mLc = 0x00;
protected byte[] mData = new byte[0];
protected int mLe = 0x00;
protected boolean mLeUsed = false;
protected TLVList tlvList = new TLVList();
public CommandApdu() {
}
public CommandApdu(int cla, int ins, int p1, int p2) {
setCommandName(ins);
mCla = cla;
mIns = ins;
mP1 = p1;
mP2 = p2;
}
public CommandApdu(int cla, int ins, int p1, int p2, byte[] data) {
setCommandName(ins);
mCla = cla;
mIns = ins;
mLc = data.length;
mP1 = p1;
mP2 = p2;
mData = data;
}
public CommandApdu(INS ins) {
setCommandName(ins.name());
mCla = ISO_CLA;
mIns = ins.Code;
mP1 = 0;
mP2 = 0;
}
public CommandApdu(int cla, int ins, int p1, int p2, byte[] data, int le) {
setCommandName(ins);
mCla = cla;
mIns = ins;
mLc = data.length;
mP1 = p1;
mP2 = p2;
mData = data;
mLe = le;
mLeUsed = true;
}
public CommandApdu(int cla, int ins, int p1, int p2, int le) {
setCommandName(ins);
mCla = cla;
mIns = ins;
mP1 = p1;
mP2 = p2;
mLe = le;
mLeUsed = true;
}
public void setCommandName(String cmdName) {
mCmdName = cmdName;
}
private void setCommandName(int ins) {
INS ins1 = INS.ByCode(ins);
if (ins1 != null) {
mCmdName = ins1.toString();
} else {
mCmdName = String.format("INS[%2X]", ins);
}
}
public String getCommandName() {
return mCmdName;
}
public void setP1(int p1) {
mP1 = p1;
}
public void setP2(int p2) {
mP2 = p2;
}
public void setData(byte[] data) {
mLc = data.length;
mData = data;
}
public void addTLV(TLV.Tag tag, byte[] value) {
tlvList.add(new TLV(tag, value));
}
public void addTLV_U8(TLV.Tag tag, int U8) {
addTLV(tag, new byte[]{(byte) U8});
}
public void addTLV_U16(TLV.Tag tag, int U16) {
addTLV(tag, Util.intToByteArray2(U16));
}
public void addTLV_U32(TLV.Tag tag, int U32) {
addTLV(tag, Util.intToByteArray4(U32));
}
public void setLe(int le) {
mLe = le;
mLeUsed = true;
}
public int getP1() {
return mP1;
}
public int getP2() {
return mP2;
}
public int getLc() {
return mLc;
}
public byte[] getData() {
return mData;
}
public TLVList getTLVs() {
return tlvList;
}
public int getLe() {
return mLe;
}
public static String toString(byte[] cmdApdu, int Lc) {
String cmd = Util.bytesToHex(cmdApdu);
if (Lc == 0) return cmd;
return cmd.substring(0, 8) + " " + cmd.substring(8, 10) + " " +
cmd.substring(10, 10 + Lc * 2) + " " + cmd.substring(10 + Lc * 2, cmd.length());
}
public void Crypt(byte[] key) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException {
if (tlvList.size() != 0) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (TLV tlv : tlvList) {
try {
tlv.WriteToStream(stream);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
mData = stream.toByteArray();
byte[] crc = Util.calculateCRC16(mData);
stream = new ByteArrayOutputStream();
stream.write(Util.intToByteArray2(mData.length));
stream.write(crc);
stream.write(mData);
mData = stream.toByteArray();
byte[] mEncryptedData = CardCrypto.Encrypt(key, mData);
mData = mEncryptedData;
mLc = mData.length;
tlvList.clear();
}
}
public byte[] toBytes() {
int length = 4; // CLA, INS, P1, P2
if (tlvList.size() != 0) {
mData = tlvList.toBytes();
mLc = mData.length;
}
if (mData.length != 0) {
length += 1; // LC
if (mLc >= 256)
length += 2;
length += mData.length; // DATA
}
if (mLeUsed) {
length += 1; // LE
if (mLc >= 256)
length += 2;
}
byte[] apdu = new byte[length];
int index = 0;
apdu[index] = (byte) mCla;
index++;
apdu[index] = (byte) mIns;
index++;
apdu[index] = (byte) mP1;
index++;
apdu[index] = (byte) mP2;
index++;
if (mLc != 0) {
if (mLc < 256) {
apdu[index] = (byte) mLc;
index++;
} else {
apdu[index] = 0;
index++;
apdu[index] = (byte) (mLc >> 8);
index++;
apdu[index] = (byte) (mLc & 0xFF);
index++;
}
System.arraycopy(mData, 0, apdu, index, mData.length);
index += mData.length;
}
if (mLeUsed) {
if (mLc < 256) {
apdu[index] += (byte) mLe; // LE
} else {
apdu[index] = 0;
index++;
apdu[index] = (byte) (mLe >> 8);
index++;
apdu[index] = (byte) (mLe & 0xFF);
index++;
}
}
return apdu;
}
public CommandApdu clone() {
CommandApdu apdu = new CommandApdu();
apdu.setCommandName(mCmdName);
apdu.mCla = mCla;
apdu.mIns = mIns;
apdu.mP1 = mP1;
apdu.mP2 = mP2;
apdu.mLc = mLc;
apdu.mData = new byte[mData.length];
System.arraycopy(mData, 0, apdu.mData, 0, mData.length);
apdu.mLe = mLe;
apdu.mLeUsed = mLeUsed;
apdu.tlvList = new TLVList(tlvList);
return apdu;
}
}

View file

@ -1,64 +0,0 @@
package com.tangem.domain.cardReader;
import android.content.Context;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tangem.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class Firmwares {
private static JsonArray jaFirmwares = null;
public static boolean needInit() {
return jaFirmwares == null;
}
public static void init(Context context) {
try (InputStream is = context.getAssets().open("fw_hashes.json")) {
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
JsonParser parser = new JsonParser();
jaFirmwares = parser.parse(reader).getAsJsonArray();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static class VerifyCodeRecord {
public String hashAlg;
public int blockIndex;
public int blockCount;
public byte[] challenge;
public byte[] digest;
}
public static VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion) throws IOException {
try {
for (int i = 0; i < jaFirmwares.size(); i++) {
JsonObject jsVersion = jaFirmwares.get(i).getAsJsonObject();
if (jsVersion.get("fw").getAsString().equals(firmwareVersion)) {
VerifyCodeRecord result = new VerifyCodeRecord();
result.hashAlg = "sha-256";
JsonArray jsHashes = jsVersion.get(result.hashAlg).getAsJsonArray();
result.challenge = Util.hexToBytes(jsVersion.get("challenge").getAsString());
int caseIndex = (Util.byteArrayToInt(Util.generateRandomBytes(4)) & 0xFFFFFF) % jsHashes.size();
JsonObject jsRecord = jsHashes.get(caseIndex).getAsJsonObject();
result.blockIndex = jsRecord.get("block").getAsInt();
result.blockCount = jsRecord.get("count").getAsInt();
result.digest = Util.hexToBytes(jsRecord.get("digest").getAsString());
return result;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.domain.cardReader;
/**
* Created by dvol on 07.03.2018.
*/
public enum INS {
Unknown(0x00),
Read(0xF2),
VerifyCard(0xF3),
ValidateCard(0xF4),
VerifyCode(0xF5),
WriteIssuerData(0xF6),
GetIssuerData(0xF7),
CreateWallet(0xF8),
CheckWallet(0xF9),
SwapPIN(0xFA),
Sign(0xFB),
PurgeWallet(0xFC),
Activate(0xFE),
OpenSession(0xFF);
INS(int Code) {
this.Code = Code;
}
public int Code;
public static INS ByCode(int Code) {
INS[] allINS = INS.values();
for (INS i : allINS) {
if (i.Code == Code) return i;
}
return Unknown;
}
}

View file

@ -1,167 +0,0 @@
package com.tangem.domain.cardReader;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import com.tangem.presentation.dialog.NfcEnableDialog;
import java.io.IOException;
public class NfcManager {
public static final String TAG = NfcManager.class.getSimpleName();
// reader mode flags: listen for type A (not B), skipping ndef check
private static final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS;
private NfcAdapter nfcAdapter;
private NfcEnableDialog mEnableNfcDialog;
private FragmentActivity activity;
private NfcAdapter.ReaderCallback mReaderCallback;
private boolean broadcomWorkaround = false;
private static final int DELAY_PRESENCE = 1500;
public NfcManager(FragmentActivity activity, NfcAdapter.ReaderCallback readerCallback) {
this.activity = activity;
mReaderCallback = readerCallback;
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
}
public void onResume() {
// register broadcast receiver
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
activity.registerReceiver(mBroadcastReceiver, filter);
if (nfcAdapter == null || !nfcAdapter.isEnabled())
showNFCEnableDialog();
else
enableReaderMode();
}
public void onPause() {
activity.unregisterReceiver(mBroadcastReceiver);
disableReaderMode();
}
public void onStop() {
if (mEnableNfcDialog != null) {
mEnableNfcDialog.dismiss();
}
}
public void ignoreTag(Tag tag) throws IOException {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// nfcAdapter.ignore(tag, 500, null, null);
// }else{
IsoDep isoDep = IsoDep.get(tag);
if (isoDep != null) {
isoDep.close();
}
// }
}
int errorCount = 0;
public void notifyReadResult(boolean success) {
// if (success) {
// errorCount = 0;
// } else {
// errorCount++;
// }
// if (errorCount >= 3) {
// disableReaderMode();
// nfcAdapter = null;
//// Toast.makeText(activity,"NFC restarted!",Toast.LENGTH_SHORT).show();
// activity.runOnUiThread(() -> {
// nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
// enableReaderMode();
// });
// }
}
private void showNFCEnableDialog() {
mEnableNfcDialog = new NfcEnableDialog();
mEnableNfcDialog.show(activity.getSupportFragmentManager(), NfcEnableDialog.Companion.getTAG());
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null)
return;
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_ON);
if (state == NfcAdapter.STATE_ON || state == NfcAdapter.STATE_TURNING_ON) {
// Log.d(TAG, "state: " + state + " , dialog: " + mEnableNfcDialog);
if (mEnableNfcDialog != null) {
mEnableNfcDialog.dismiss();
}
if (state == NfcAdapter.STATE_ON) {
enableReaderMode();
}
} else {
if (mEnableNfcDialog == null || !mEnableNfcDialog.isVisible()) {
showNFCEnableDialog();
}
}
}
}
};
@TargetApi(Build.VERSION_CODES.KITKAT)
private void enableReaderMode() {
Bundle options = new Bundle();
if (broadcomWorkaround) {
/* This is a work around for some Broadcom chipsets that does
* the presence check by sending commands that interrupt the
* processing of the ongoing command.
*/
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, DELAY_PRESENCE);
}
nfcAdapter.enableReaderMode(activity, mReaderCallback, READER_FLAGS, options);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void disableReaderMode() {
if (nfcAdapter != null) {
nfcAdapter.disableReaderMode(activity);
}
}
private static final int REQUEST_NFC_PERMISSIONS = 1;
private static String[] PERMISSIONS_NFC = {
Manifest.permission.NFC
};
// checks if the app has NFC permission if the app does not has permission then the user will be prompted to grant permissions
public static void verifyPermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.NFC);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_NFC,
REQUEST_NFC_PERMISSIONS
);
}
}
}

View file

@ -1,103 +0,0 @@
package com.tangem.domain.cardReader;
import org.spongycastle.crypto.CipherParameters;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.macs.HMac;
import org.spongycastle.crypto.params.KeyParameter;
import java.security.InvalidKeyException;
import java.util.Arrays;
public final class PBKDF2 {
private static final HMac F =new HMac(new SHA256Digest());
/**
* Derive a key.
*
* @param password The password to derive the key from.
* @param iterations The iteration count.
* @return Returns a key derived with the specified parameters.
* @throws InvalidKeyException If the specified length for the derived key
* is to long.
*/
public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations) throws InvalidKeyException {
return deriveKey(password, salt, iterations, F.getMacSize());
}
/**
* Derive a key with a specified length.
*
* @param password The password to derive the key from.
* @param iterations The iteration count.
* @param len The length of the derived key.
* @return Returns a key derived with the specified parameters.
* @throws InvalidKeyException If the specified length for the derived key
* is to long.
*/
public static byte[] deriveKey(final byte[] password, final byte[] salt, final int iterations, final int len) throws InvalidKeyException {
// Check key length
if (len > ((Math.pow(2, 32) - 1) * F.getMacSize()))
throw new InvalidKeyException("Derived key to long");
byte[] derivedKey = new byte[len];
final int J = 0;
final int K = F.getMacSize();
final int U = F.getMacSize() << 1;
final int B = K + U;
final byte[] workingArray = new byte[K + U + 4];
// Initialize F
CipherParameters macParams = new KeyParameter(password);
F.init(macParams);
// Perform iterations
for (int kpos = 0, blk = 1; kpos < len; kpos += K, blk++) {
storeInt32BE(blk, workingArray, B);
F.update(salt, 0, salt.length);
F.reset();
F.update(salt, 0, salt.length);
F.update(workingArray, B, 4);
F.doFinal(workingArray, U);
System.arraycopy(workingArray, U, workingArray, J, K);
for (int i = 1, j = J, k = K; i < iterations; i++) {
F.init(macParams);
F.update(workingArray, j, K);
F.doFinal(workingArray, k);
for (int u = U, v = k; u < B; u++, v++)
workingArray[u] ^= workingArray[v];
int swp = k;
k = j;
j = swp;
}
int tocpy = Math.min(len - kpos, K);
System.arraycopy(workingArray, U, derivedKey, kpos, tocpy);
}
Arrays.fill(workingArray, (byte) 0);
return derivedKey;
}
/**
* Convert a 32-bit integer value into a big-endian byte array
*
* @param value The integer value to convert
* @param bytes The byte array to store the converted value
* @param offSet The offset in the output byte array
*/
public static void storeInt32BE(int value, byte[] bytes, int offSet) {
bytes[offSet + 3] = (byte) (value);
bytes[offSet + 2] = (byte) (value >>> 8);
bytes[offSet + 1] = (byte) (value >>> 16);
bytes[offSet] = (byte) (value >>> 24);
}
}

View file

@ -1,137 +0,0 @@
package com.tangem.domain.cardReader;
import com.tangem.util.Util;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
public class ResponseApdu {
private int mSw1 = 0x00;
private int mSw2 = 0x00;
private byte[] mData = new byte[0];
private byte[] mBytes = new byte[0];
private TLVList tlvList = new TLVList();
private String parseError = null;
private ResponseApdu() {
}
ResponseApdu(byte[] respApdu) {
if (respApdu.length < 2) {
return;
}
if (respApdu.length > 2) {
mData = new byte[respApdu.length - 2];
System.arraycopy(respApdu, 0, mData, 0, respApdu.length - 2);
try {
tlvList = TLVList.fromBytes(mData);
} catch (TLVException e) {
parseError = e.getMessage();
}
}else{
tlvList=new TLVList();
parseError=null;
}
mSw1 = 0x00FF & respApdu[respApdu.length - 2];
mSw2 = 0x00FF & respApdu[respApdu.length - 1];
mBytes = respApdu;
}
public static boolean isStatusWord(byte[] respApdu, int SW)
{
int mSw1 = 0x00FF & respApdu[respApdu.length - 2];
int mSw2 = 0x00FF & respApdu[respApdu.length - 1];
return ((mSw1 << 8) | mSw2)==SW;
}
public static ResponseApdu Decrypt(byte[] data, byte[] key) throws Exception{
if( data.length==2 )
{
ResponseApdu responseApdu = new ResponseApdu();
responseApdu.mSw1 = ((int) data[0] & 0xFF);
responseApdu.mSw2 = ((int) data[1] & 0xFF);
return responseApdu;
}else if( data.length>=18 ){
byte[] decryptedData = CardCrypto.Decrypt(key, Arrays.copyOfRange(data, 0, data.length - 2),true);
ByteArrayInputStream inputStream = new ByteArrayInputStream(decryptedData);
byte[] baLength = new byte[2];
inputStream.read(baLength);
int length = ((int) baLength[0] & 0xFF) * 256 + ((int) baLength[1] & 0xFF);
if (length > decryptedData.length - 4)
throw new Exception("Can't decrypt - data size invalid");
byte[] baCRC = new byte[2];
inputStream.read(baCRC);
byte[] answerData = new byte[length];
inputStream.read(answerData);
byte[] crc = Util.calculateCRC16(answerData);
if (!Arrays.equals(baCRC, crc)) throw new Exception("Can't decrypt - crc invalid");
ResponseApdu responseApdu = new ResponseApdu();
responseApdu.mSw1 = ((int) data[data.length - 2] & 0xFF);
responseApdu.mSw2 = ((int) data[data.length - 1] & 0xFF);
responseApdu.mBytes = data;
responseApdu.mData = answerData;
try {
responseApdu.tlvList = TLVList.fromBytes(answerData);
} catch (TLVException e) {
responseApdu.parseError = e.getMessage();
}
return responseApdu;
}else{
throw new Exception("Can't decrypt - data size to small");
}
}
public int getSW1() {
return mSw1;
}
public int getSW2() {
return mSw2;
}
public int getSW1SW2() {
return (mSw1 << 8) | mSw2;
}
public byte[] getData() {
return mData;
}
public TLVList getTLVs() {
return tlvList;
}
public boolean isParsedWithError() {
return parseError != null;
}
public String getParseErroMessage() {
return parseError;
}
public byte[] toBytes() {
return mBytes;
}
public boolean isStatus(int sw1sw2) {
if (getSW1SW2() == sw1sw2) {
return true;
} else {
return false;
}
}
public String getSW1SW2Description() {
return SW.getDescription(getSW1SW2());
}
}

View file

@ -1,43 +0,0 @@
package com.tangem.domain.cardReader;
/**
* Created by dvol on 07.03.2018.
*/
public class SW {
public static final int PROCESS_COMPLETED = 0x9000;
public static final int INVALID_PARAMS = 0x6A86;
public static final int ERROR_PROCESSING_COMMAND = 0x6286;
public static final int INVALID_STATE = 0x6985;
public static final int PINS_NOT_CHANGED = PROCESS_COMPLETED;
public static final int PIN1_CHANGED = PROCESS_COMPLETED + 0x0001;
public static final int PIN2_CHANGED = PROCESS_COMPLETED + 0x0002;
public static final int PINS_CHANGED = PROCESS_COMPLETED + 0x0003;
public static final int INS_NOT_SUPPORTED = 0x6D00;
public static final int NEED_ENCRYPTION = 0x6982;
public static final int NEED_PAUSE = 0x9789;
public static String getDescription(int sw) {
switch (sw) {
case ERROR_PROCESSING_COMMAND:
return "SW_ERROR_PROCESSING_COMMAND";
case INVALID_PARAMS:
return "SW_INVALID_PARAMS";
case INVALID_STATE:
return "SW_INVALID_STATE";
case INS_NOT_SUPPORTED:
return "SW_INS_NOT_SUPPORTED";
case NEED_ENCRYPTION:
return "SW_NEED_ENCRYPTION";
case PIN1_CHANGED:
return "SW_PIN1_CHANGED";
case PIN2_CHANGED:
return "SW_PIN2_CHANGED";
case PINS_CHANGED:
return "SW_PINS_CHANGED";
case PROCESS_COMPLETED:
return "SW_PROCESS_COMPLETED";
}
return "???";
}
}

View file

@ -1,27 +0,0 @@
package com.tangem.domain.cardReader;
/**
* Created by dvol on 07.03.2018.
*/
public class SettingsMask {
public static final int IsReusable = 0x0001;
public static final int UseActivation = 0x0002;
public static final int UseBlock = 0x0008;
public static final int AllowSwapPIN = 0x0010;
public static final int AllowSwapPIN2 = 0x0020;
public static final int UseCVC = 0x0040;
public static final int ForbidDefaultPIN = 0x0080;
public static final int UseOneCommandAtTime = 0x0100;
public static final int UseNDEF = 0x0200;
public static final int UseDynamicNDEF = 0x0400;
public static final int SmartSecurityDelay = 0x0800;
public static final int Protocol_AllowUnencrypted = 0x1000;
public static final int Protocol_AllowStaticEncryption = 0x2000;
public static final int ProtectIssuerDataAgainstReplay = 0x4000;
}

View file

@ -1,264 +0,0 @@
package com.tangem.domain.cardReader;
import com.tangem.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* Created by dvol on 23.06.2017.
*/
public class TLV {
public enum Tag {
TAG_Unknown(0x00),
TAG_CardID(0x01),
TAG_Status(0x02),
TAG_CardPublicKey(0x03),
TAG_CardSignature(0x04),
TAG_CurveID(0x05),
TAG_HashAlgID(0x06),
TAG_SigningMethod(0x07),
TAG_MaxSignatures(0x08),
TAG_PauseBeforePIN2(0x09),
TAG_SettingsMask(0x0A),
TAG_CardData(0x0C),
TAG_NDEFData(0x0D),
TAG_Health(0x0F),
TAG_PIN(0x10),
TAG_PIN2(0x11),
TAG_NewPIN(0x12),
TAG_NewPIN2(0x13),
TAG_NewPIN_Hash(0x14),
TAG_NewPIN2_Hash(0x15),
TAG_Challenge(0x16),
TAG_Salt(0x17),
TAG_ValidationCounter(0x18),
TAG_CVC(0x19),
TAG_Session_Key_A(0x1A),
TAG_Session_Key_B(0x1B),
TAG_Pause(0x1C),
TAG_Manufacture_ID(0x20),
TAG_Manufacturer_Signature(0x21),
TAG_Issuer_Data_PublicKey(0x30),
TAG_Issuer_Transaction_PublicKey(0x31),
TAG_Issuer_Data(0x32),
TAG_Issuer_Data_Signature(0x33),
TAG_Issuer_Transaction_Signature(0x34),
TAG_Issuer_Data_Counter(0x35),
TAG_IsActivated(0x3A),
TAG_ActivationSeed(0x3B),
TAG_ResetPIN(0x36),
TAG_CodePageAddress(0x40),
TAG_CodePageCount(0x41),
TAG_CodeHash(0x42),
TAG_TrOut_Hash(0x50),
TAG_TrOut_HashSize(0x51),
TAG_TrOut_Raw(0x52),
TAG_Wallet_PublicKey(0x60),
TAG_Signature(0x61),
TAG_RemainingSignatures(0x62),
TAG_SignedHashes(0x63),
TAG_Firmware(0x80),
TAG_Batch(0x81),
TAG_ManufactureDateTime(0x82),
TAG_Issuer_ID(0x83),
TAG_Blockchain_ID(0x84),
TAG_Manufacturer_PublicKey(0x85),
TAG_CardID_Manufacturer_Signature(0x86),
TAG_Token_Symbol(0xA0),
TAG_Token_Contract_Address(0xA1),
TAG_Token_Decimal(0xA2),
TAG_Denomination(0xC0),
TAG_ValidatedBalance(0xC1),
TAG_LastSign_Date(0xC2),
TAG_DenominationText(0xC3);
Tag(int Code) {
this.Code = Code;
}
public int getCode() {
return Code;
}
private int Code;
public static Tag ByCode(int Code) {
Tag[] allTags = Tag.values();
for (Tag t : allTags) if (t.getCode() == Code) return t;
return TAG_Unknown;
}
}
private Tag tag;
public Tag getTag() {
return tag;
}
public byte[] Value;
public TLV(Tag tag, byte[] value) {
this.tag = tag;
this.Value = value;
}
public void WriteToStream(ByteArrayOutputStream stream) throws IOException {
stream.write(tag.getCode());
if (Value != null) {
if (Value.length > 0xFE) {
stream.write(0xFF);
stream.write((Value.length >> 8) & 0xFF);
stream.write(Value.length & 0xFF);
} else {
stream.write(Value.length & 0xFF);
}
stream.write(Value);
} else {
stream.write(0x00);
}
}
public static TLV ReadFromStream(ByteArrayInputStream stream) throws IOException {
int code = stream.read();
if (code == -1) return null;
int len = stream.read();
if (len == -1)
throw new IOException("Can't read TLV");
if (len == 0xFF) {
int lenH = stream.read();
if (lenH == -1)
throw new IOException("Can't read TLV");
len = stream.read();
if (len == -1)
throw new IOException("Can't read TLV");
len |= (lenH << 8);
}
byte[] value = new byte[len];
if (len > 0) {
if (len != stream.read(value)) {
throw new IOException("Can't read TLV");
}
}
Tag tag = Tag.ByCode(code);
TLV result = new TLV(tag, value);
return result;
}
public int getAsInt() {
return Util.byteArrayToInt(Value);
}
public String getAsHexString() {
return Util.bytesToHex(Value);
}
public String getAsString() {
if( Value.length==0 ) return "";
if (Value[Value.length - 1] == 0) {
String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8"));
return s1.trim();
} else {
String s1 = new String(Value, Charset.forName("utf-8"));
return s1.trim();
}
}
@Override
public String toString() {
switch (tag) {
case TAG_CardData:
case TAG_Issuer_Data: {
try {
TLVList tlvSub = TLVList.fromBytes(Value);
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), tlvSub.toString());
} catch (TLVException e) {
e.printStackTrace();
}
if (Value != null) {
return String.format("%s[%d]: %s (non TLV)", tag.name(), Value.length, Util.bytesToHex(Value));
} else {
return String.format("%s[]: [[NULL]]", tag.name());
}
}
case TAG_CurveID:
case TAG_HashAlgID:
case TAG_Blockchain_ID:
case TAG_Manufacture_ID:
case TAG_Firmware:
case TAG_Issuer_ID:
case TAG_Token_Symbol:
if (Value != null) {
return String.format("%s[%d]: %s(%s)", tag.name(), Value.length, Util.bytesToHex(Value), getAsString());
} else {
return String.format("%s[]: [[NULL]]", tag.name());
}
case TAG_SettingsMask: {
StringBuilder sb=new StringBuilder();
if( Value!=null ) {
try {
int iValue = Util.byteArrayToInt(Value);
sb.append("[");
if ((iValue & SettingsMask.AllowSwapPIN) != 0) sb.append("AllowSwapPIN, ");
if ((iValue & SettingsMask.AllowSwapPIN2) != 0)
sb.append("AllowSwapPIN2, ");
if ((iValue & SettingsMask.ForbidDefaultPIN) != 0)
sb.append("ForbidDefaultPIN, ");
if ((iValue & SettingsMask.IsReusable) != 0) sb.append("IsReusable, ");
if ((iValue & SettingsMask.Protocol_AllowStaticEncryption) != 0)
sb.append("Protocol_AllowStaticEncryption, ");
if ((iValue & SettingsMask.Protocol_AllowUnencrypted) != 0)
sb.append("Protocol_AllowUnencrypted, ");
if ((iValue & SettingsMask.SmartSecurityDelay) != 0)
sb.append("SmartSecurityDelay, ");
if ((iValue & SettingsMask.UseActivation) != 0)
sb.append("UseActivation, ");
if ((iValue & SettingsMask.UseBlock) != 0) sb.append("UseBlock, ");
if ((iValue & SettingsMask.UseCVC) != 0) sb.append("UseCVC, ");
if ((iValue & SettingsMask.UseDynamicNDEF) != 0)
sb.append("UseDynamicNDEF, ");
if ((iValue & SettingsMask.UseNDEF) != 0) sb.append("UseNDEF, ");
if ((iValue & SettingsMask.UseOneCommandAtTime) != 0)
sb.append("UseOneCommandAtTime, ");
if ((iValue & SettingsMask.ProtectIssuerDataAgainstReplay) != 0)
sb.append("ProtectIssuerDataAgainstReplay, ");
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
sb.append("]");
return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), sb.toString());
}
catch (Exception e)
{
e.printStackTrace();
return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value));
}
}else{
return String.format("%s[]: [[NULL]]", tag.name());
}
}
default:
if (Value != null) {
return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value));
} else {
return String.format("%s[]: [[NULL]]", tag.name());
}
}
}
}

View file

@ -1,18 +0,0 @@
package com.tangem.domain.cardReader;
public class TLVException extends Exception {
private static final long serialVersionUID = 1L;
public TLVException(String message){
super(message);
}
public TLVException(String message, Throwable cause) {
super(message, cause);
}
public TLVException(Throwable cause) {
super(cause);
}
}

View file

@ -1,74 +0,0 @@
package com.tangem.domain.cardReader;
/**
* Created by dvol on 23.06.2017.
*/
import android.support.annotation.NonNull;
import com.tangem.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
public class TLVList extends ArrayList<TLV> {
public String getParsedTLVs(String Prefix) {
String parsed = "";
for (int i = 0; i < size(); i++) {
parsed += Prefix + this.get(i).toString() + (i < size() - 1 ? "\n" : "");
}
return parsed;//.substring(0,parsed.length()-2);
}
public TLVList() {
super();
}
public TLVList(@NonNull Collection<? extends TLV> c) {
super(c);
}
public TLV getTLV(TLV.Tag tag) {
for (TLV tlv : this) {
if (tlv.getTag() == tag) return tlv;
}
return null;
}
public int getTagAsInt(TLV.Tag tag) {
TLV tlv = getTLV(tag);
return Util.byteArrayToInt(tlv.Value);
}
public byte[] toBytes() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (TLV tlv : this) {
try {
tlv.WriteToStream(stream);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
return stream.toByteArray();
}
public static TLVList fromBytes(byte[] mData) throws TLVException {
TLVList tlvList = new TLVList();
ByteArrayInputStream stream = new ByteArrayInputStream(mData);
TLV tlv = null;
do {
try {
tlv = TLV.ReadFromStream(stream);
if (tlv != null) tlvList.add(tlv);
} catch (IOException e) {
throw new TLVException("TLVError: " + e.getMessage());
}
}
while (tlv != null);
return tlvList;
}
}

View file

@ -11,21 +11,14 @@ import com.tangem.domain.wallet.btc.BitcoinOutputStream;
import com.tangem.domain.wallet.btc.BtcData;
import com.tangem.util.CryptoUtil;
import com.tangem.util.FormatUtil;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
@SuppressWarnings({"WeakerAccess", "TryWithIdenticalCatches", "unused"})
public final class BTCUtils {

View file

@ -1,5 +1,6 @@
package com.tangem.domain.wallet;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.wallet.R;
public class BalanceValidator {

View file

@ -1,103 +0,0 @@
package com.tangem.domain.wallet;
import com.google.common.base.Strings;
import com.tangem.wallet.R;
/**
* Created by dvol on 06.08.2017.
*/
public enum Blockchain {
Unknown("", "", 1.0, R.drawable.ic_logo_unknown, ""),
Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
Token("Token", "ERC20", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash");
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
mID = ID;
mCurrency = currency;
// mMultiplier = multiplier;
mImageResource = imageResource;
mOfficialName = officialName;
}
private String mID, mOfficialName;
//private double mMultiplier;
private String mCurrency;
private int mImageResource;
public String getID() {
return mID;
}
public String getOfficialName() {
return mOfficialName;
}
// public double getMultiplier() {
// return mMultiplier;
// }
public String getCurrency() {
return mCurrency;
}
public static Blockchain fromId(String id) {
for (Blockchain blockchain : values()) {
if (blockchain.getID().equals(id)) return blockchain;
}
return null;
}
public static Blockchain fromCurrency(String currency) {
for (Blockchain blockchain : values()) {
if (blockchain.getCurrency() == currency) return blockchain;
}
return null;
}
public static String[] getCurrencies() {
String[] result = new String[values().length - 1];
for (int i = 0; i < result.length - 1; i++) {
result[i] = values()[i + 1].getCurrency();
}
return result;
}
private int getImageResource() {
return mImageResource;
}
public int getImageResource(android.content.Context context, String name) {
if (Strings.isNullOrEmpty(name))
return getImageResource();
name = name.toLowerCase();
int resourceId = context.getResources().getIdentifier(name + "_token", "drawable", context.getPackageName());
if (resourceId <= 0)
return R.drawable.ic_logo_ethereum;
return resourceId;
}
public static int getLogoImageResource(String blockchainID, String symbolName) {
switch (blockchainID) {
case "BTC":
return R.drawable.ic_logo_bitcoin;
case "Token":
if (symbolName.equals("SEED"))
return R.drawable.ic_logo_seed;
else
return R.drawable.ic_logo_ethereum;
case "ETH":
return R.drawable.ic_logo_ethereum;
}
return R.drawable.tangem2;
}
}

View file

@ -3,8 +3,8 @@ package com.tangem.domain.wallet;
import android.os.Bundle;
import android.util.Log;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.tangem.tangemcard.data.Blockchain;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class CoinData {
@ -12,6 +12,24 @@ public abstract class CoinData {
public CoinData() {
}
private String wallet;
public void setWallet(String wallet) {
this.wallet = wallet;
}
public String getWallet() {
return wallet;
}
public String getShortWalletString() {
if (wallet.length() < 22) {
return wallet;
} else {
return wallet.substring(0, 10) + "......" + wallet.substring(wallet.length() - 10, wallet.length());
}
}
public boolean isBalanceReceived() {
return balanceReceived;
}
@ -23,6 +41,8 @@ public abstract class CoinData {
}
public void loadFromBundle(Bundle B) {
wallet = B.getString("Wallet");
if (B.containsKey("balanceReceived")) setBalanceReceived(B.getBoolean("balanceReceived"));
validationNodeDescription = B.getString("validationNodeDescription");
@ -40,6 +60,8 @@ public abstract class CoinData {
public void saveToBundle(Bundle B) {
try {
B.putString("Wallet", wallet);
if (balanceEqual != null) B.putBoolean("isBalanceEqual", balanceEqual);
if (failedBalanceRequestCounter != null)

View file

@ -3,7 +3,7 @@ package com.tangem.domain.wallet;
import android.net.Uri;
import android.text.InputFilter;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.tangemcard.reader.CardProtocol;
import java.math.BigDecimal;
import java.math.BigInteger;

View file

@ -6,6 +6,7 @@ import com.tangem.domain.wallet.btc.BtcEngine
import com.tangem.domain.wallet.eth.EthEngine
import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.tangemcard.data.Blockchain
/**
* Factory for create specific engine

View file

@ -1,144 +0,0 @@
package com.tangem.domain.wallet;
import android.content.Context;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.tangem.domain.cardReader.CardCrypto;
import com.tangem.util.Util;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by dvol on 14.11.2017.
*/
public class Issuer {
static class KeyPair {
String privateKey;
String publicKey;
byte[] getPrivateKey() throws Exception {
if (privateKey != null) {
return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey));
} else {
throw new Exception("No private key!");
}
}
byte[] getPublicKey() throws Exception {
if (publicKey == null) {
if (privateKey != null) {
return CardCrypto.GeneratePublicKey(Util.hexToBytes(privateKey));
} else {
throw new Exception("Invalid key format: no public and no private");
}
} else {
return Util.hexToBytes(publicKey);
}
}
}
private String id;
private String officialName;
private KeyPair dataKey;
private KeyPair transactionKey;
public String getID() {
return id;
}
private static List<Issuer> instances = new ArrayList<>();
public static boolean needInit() {
return instances.size() == 0;
}
public static void init(Context context) {
try {
Issuer unknown = new Issuer();
unknown.id = "UNKNOWN";
unknown.officialName = "UNKNOWN";
try (InputStream is = context.getAssets().open("issuers.json")) {
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Type listType = new TypeToken<List<Issuer>>() {
}.getType();
instances = new Gson().fromJson(reader, listType);
}
}
instances.add(0, unknown);
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] getPublicDataKey() throws Exception {
if (dataKey == null)
throw new Exception("Data key not specified!");
return dataKey.getPublicKey();
}
public byte[] getPublicTransactionKey() throws Exception {
if (dataKey == null)
throw new Exception("Transaction key not specified!");
return transactionKey.getPublicKey();
}
public byte[] getPrivateDataKey() throws Exception {
if (dataKey == null)
throw new Exception("Data key not specified!");
return dataKey.getPrivateKey();
}
public byte[] getPrivateTransactionKey() throws Exception {
if (transactionKey == null)
throw new Exception("Transaction key not specified!");
return transactionKey.getPrivateKey();
}
public String getOfficialName() {
return officialName != null ? officialName : id;
}
public static Issuer FindIssuer(String ID) {
for (int i = 0; i < instances.size(); i++) {
try {
if (instances.get(i).id.equals(ID)) {
return instances.get(i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return Unknown();
}
public static Issuer FindIssuer(String ID, byte[] publicDataKey) {
for (int i = 1; i < instances.size(); i++) {
try {
if (instances.get(i).id.equals(ID) && Arrays.equals(instances.get(i).getPublicDataKey(), publicDataKey)) {
return instances.get(i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return Unknown();
}
public static Issuer Unknown() {
return instances.get(0);
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.domain.wallet;
/**
* Created by dvol on 09.08.2017.
*/
public enum Manufacturer {
Unknown("", "Unknown"),
SMARTCASH_AG("SMART CASH AG", "SMART CASH AG"),
DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG", "SMART CASH AG (DEVELOPERS)"),
SMARTCASH("SMART CASH", "SMART CASH");
private String ID;
private String officialName;
Manufacturer(String id, String officialName) {
this.ID = id;
this.officialName = officialName;
}
public String getOfficialName() {
return officialName;
}
public static Manufacturer FindManufacturer(String ID) {
Manufacturer[] manufacturers = Manufacturer.values();
for (int i = 1; i < manufacturers.length; i++) {
if (manufacturers[i].ID.equals(ID)) {
return manufacturers[i];
}
}
return Manufacturer.Unknown;
}
}

View file

@ -1,811 +0,0 @@
package com.tangem.domain.wallet;
import android.os.Bundle;
import android.util.Log;
import com.google.common.base.Strings;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.cardReader.SettingsMask;
import com.tangem.util.Util;
import java.util.Date;
/**
* Created by dvol on 16.07.2017.
*/
public class TangemCard {
public static final String EXTRA_CARD = "Card";
public static final String EXTRA_UID = "UID";
private byte[] CID;
private Status status;
private String wallet;
private String UID;
private String blockchainID;
private Manufacturer manufacturer = Manufacturer.Unknown;
private boolean manufacturerConfirmed = false;
private int maxSignatures;
private int remainingSignatures;
private String PIN;
private byte[] pbWalletKey = null;
private byte[] pbCardKey = null;
private byte[] pbWalletKeyRar = null;
private Date dtPersonalization = null;
public String getBlockchainID() {
return blockchainID;
}
public Blockchain getBlockchain() {
return Blockchain.fromId(blockchainID);
}
public void setBlockchain(Blockchain blockchain) {
blockchainID=blockchain.getID();
}
public void setBlockchainID(String blockchainID) {
this.blockchainID = blockchainID;
}
public void addTokenToBlockchainName() {
String token = getTokenSymbol();
if (Strings.isNullOrEmpty(token))
return;
String oldName = getBlockchain().getOfficialName();
//String newName = String.format("%s - %s ERC20 token", token, oldName);
String newName = token + " <br><small><small> " + oldName + " ERC20 token</small></small>";
blockchainName=newName;
}
private String blockchainName = "";
public String getBlockchainName() {
if (Strings.isNullOrEmpty(blockchainName))
return getBlockchain().getOfficialName();
return blockchainName;
}
public void setBlockchainIDFromCard(String blockchainID) {
if (Blockchain.fromId(blockchainID) != Blockchain.Ethereum && Blockchain.fromId(blockchainID) != Blockchain.EthereumTestNet)
this.blockchainID = blockchainID;
if (isToken()) {
this.blockchainID = Blockchain.Token.getID();
addTokenToBlockchainName();
} else {
this.blockchainID = blockchainID;
}
}
public void setWalletPublicKey(byte[] publicKey) {
pbWalletKey = publicKey;
}
public void setWalletPublicKeyRar(byte[] publicKey) {
pbWalletKeyRar = publicKey;
}
public byte[] getWalletPublicKey() {
return pbWalletKey;
}
public byte[] getWalletPublicKeyRar() {
return pbWalletKeyRar;
}
private boolean walletPublicKeyValid = false;
public void setWalletPublicKeyValid(boolean walletPublicKeyValid) {
this.walletPublicKeyValid = walletPublicKeyValid;
}
public boolean isWalletPublicKeyValid() {
return walletPublicKeyValid;
}
public void setCardPublicKey(byte[] publicKey) {
pbCardKey = publicKey;
}
public byte[] getCardPublicKey() {
return pbCardKey;
}
private boolean cardPublicKeyValid = false;
public void setCardPublicKeyValid(boolean cardPublicKeyValid) {
this.cardPublicKeyValid = cardPublicKeyValid;
}
public boolean isCardPublicKeyValid() {
return cardPublicKeyValid;
}
public Manufacturer getManufacturer() {
return manufacturer;
}
public boolean isManufacturerConfirmed() {
return manufacturerConfirmed;
}
public void setManufacturer(Manufacturer manufacturer, boolean verified) {
if (this.manufacturer == manufacturer) {
this.manufacturerConfirmed |= verified;
} else {
this.manufacturer = manufacturer;
this.manufacturerConfirmed = verified;
}
}
private Boolean codeConfirmed;
public void setCodeConfirmed(Boolean codeConfirmed) {
this.codeConfirmed = codeConfirmed;
}
public Boolean isCodeConfirmed() {
return codeConfirmed;
}
private Boolean onlineVerified;
public void setOnlineVerified(Boolean verified) {
this.onlineVerified = verified;
}
public Boolean isOnlineVerified() {
return onlineVerified;
}
private Boolean onlineValidated;
public void setOnlineValidated(Boolean validated) {
this.onlineValidated = validated;
}
public Boolean isOnlineValidated() {
return onlineValidated;
}
public int getRemainingSignatures() {
return remainingSignatures;
}
public void setRemainingSignatures(int remainingSignatures) {
this.remainingSignatures = remainingSignatures;
}
public String getPIN() {
return PIN;
}
public void setPIN(String PIN) {
this.PIN = PIN;
}
public void switchToInitialBlockchain() {
if (tokenSymbol.length() > 1)
blockchainID = Blockchain.Token.getID(); // Reset blockchain to Token from ETH for token cards with zero token balance on it
}
public Date getPersonalizationDateTime() {
return dtPersonalization;
}
public void setPersonalizationDateTime(Date dtPersonalization) {
this.dtPersonalization = dtPersonalization;
}
public String getPersonalizationDateTimeDescription() {
return Util.formatDate(dtPersonalization);
}
private int health = 0;
public void setHealth(int health) {
if (health > this.health) this.health = health;
}
public boolean isHealthOK() {
return health == 0;
}
private Issuer issuer = Issuer.Unknown();
private byte[] issuerPublicDataKey = null;
// public void setIssuer(Issuer issuer) {
// this.issuer = issuer;
// }
public void setIssuer(String issuerID, byte[] issuerPublicDataKey) {
this.issuerPublicDataKey = issuerPublicDataKey;
this.issuer = Issuer.FindIssuer(issuerID, issuerPublicDataKey);
}
public Issuer getIssuer() {
return issuer;
}
public byte[] getIssuerPublicDataKey() {
return issuerPublicDataKey;
}
public String getIssuerDescription() {
return issuer.getOfficialName();
}
String contractAddress = "";
public void setContractAddress(String address) {
contractAddress = address;
}
public String getContractAddress() {
return contractAddress;
}
public String tokenSymbol = "";
public void setTokenSymbol(String symbol) {
tokenSymbol = symbol;
}
public String getTokenSymbol() {
return tokenSymbol;
}
public boolean isToken() {
return !Strings.isNullOrEmpty(tokenSymbol);
}
int tokensDecimal = 18;
public void setTokensDecimal(int tokensDecimal) {
this.tokensDecimal = tokensDecimal;
}
public int getTokensDecimal() {
return tokensDecimal;
}
private byte[] issuerData;
private byte[] issuerDataSignature;
public byte[] getIssuerData() {
return issuerData;
}
public byte[] getIssuerDataSignature() {
return issuerDataSignature;
}
public void setIssuerData(byte[] value, byte[] signature) {
issuerData = value;
issuerDataSignature = signature;
}
private boolean needWriteIssuerData = false;
public boolean getNeedWriteIssuerData() {
return needWriteIssuerData;
}
public void setNeedWriteIssuerData(boolean value) {
needWriteIssuerData = value;
}
public String getIssuerDataDescription() {
return "";
}
private int pauseBeforePIN2 = 0;
public void setPauseBeforePIN2(int value) {
this.pauseBeforePIN2 = value;
}
public int getPauseBeforePIN2() {
return pauseBeforePIN2;
}
private Integer settingsMask = null;
public void setSettingsMask(int settingsMask) {
this.settingsMask = settingsMask;
}
public Boolean isReusable() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.IsReusable) != 0;
}
public Boolean allowSwapPIN() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.AllowSwapPIN) != 0;
}
public Boolean allowSwapPIN2() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.AllowSwapPIN2) != 0;
}
public Boolean needCVC() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.UseCVC) != 0;
}
public Boolean useDefaultPIN1() {
return PINStorage.isDefaultPIN(getPIN());
}
public Boolean useSmartSecurityDelay() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.SmartSecurityDelay) != 0;
}
public enum PIN2_Mode {Unchecked, DefaultPIN2, CustomPIN2}
public PIN2_Mode PIN2 = PIN2_Mode.Unchecked;
public Boolean useDefaultPIN2() {
if (PIN2 == PIN2_Mode.DefaultPIN2 || (PIN2 == PIN2_Mode.Unchecked && (needCVC() || (getPauseBeforePIN2() > 0)))) {
// define that we use default PIN2 if we try it or not try and security delay or CVC is used
return true;
} else {
return false;
}
}
public void setUseDefaultPIN2(Boolean value) {
if (value != null) {
PIN2 = value ? PIN2_Mode.DefaultPIN2 : PIN2_Mode.CustomPIN2;
} else {
PIN2 = PIN2_Mode.Unchecked;
}
}
public Boolean supportNDEF() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.UseNDEF) != 0;
}
public Boolean supportOnlyOneCommandAtTime() {
if (settingsMask == null) return null;
return supportNDEF() && ((settingsMask & SettingsMask.UseOneCommandAtTime) != 0);
}
public Boolean supportDynamicNDEF() {
if (settingsMask == null) return null;
return supportNDEF() && ((settingsMask & SettingsMask.UseDynamicNDEF) != 0);
}
public Boolean supportBlock() {
if (settingsMask == null) return null;
return (settingsMask & SettingsMask.UseBlock) != 0;
}
public int getMaxSignatures() {
return maxSignatures;
}
public void setMaxSignatures(int value) {
maxSignatures = value;
}
private String firmwareVersion;
public void setFirmwareVersion(String firmwareVersion) {
this.firmwareVersion = firmwareVersion;
}
public String getFirmwareVersion() {
return firmwareVersion;
}
public Boolean useDevelopersFirmware() {
return getFirmwareVersion().endsWith("d") || getFirmwareVersion().endsWith("SDK");
}
private static String getFirmwareVersionNumber(String version) throws Exception {
if (version == null || version.length() < 4) {
throw new Exception("Firmware version has unsupported format!");
}
if (version.endsWith("d SDK")) {
return version.substring(0, version.length() - 5);
} else if (version.endsWith("r")) {
return version.substring(0, version.length() - 1);
} else {
return version;
}
}
private static int[] getFirmwareVersionNumbers(String version) throws Exception {
String fwNumber = getFirmwareVersionNumber(version);
String[] strNumbers = fwNumber.split("\\.");
if (strNumbers.length != 2) throw new Exception("Firmware version has unsupported format!");
try {
int major = Integer.parseInt(strNumbers[0]), minor = Integer.parseInt(strNumbers[1]);
return new int[]{major, minor};
} catch (NumberFormatException e) {
e.printStackTrace();
throw new Exception("Firmware version has unsupported format!");
}
}
public Boolean isFirmwareOlder(String version) throws Exception {
int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version);
return numbers1[0] < numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] < numbers2[1]);
}
public Boolean isFirmwareNewer(String version) throws Exception {
int[] numbers1 = getFirmwareVersionNumbers(firmwareVersion), numbers2 = getFirmwareVersionNumbers(version);
return numbers1[0] > numbers2[0] || (numbers1[0] == numbers2[0] && numbers1[1] > numbers2[1]);
}
private String batch;
public void setBatch(String batch) {
this.batch = batch;
}
public String getBatch() {
return batch;
}
public enum SigningMethod {
Sign_Hash(0, "sign hash"),
Sign_Raw(1, "sign raw tx"),
Sign_Hash_Validated_By_Issuer(2, "sign hash validated by issuer"),
Sign_Raw_Validated_By_Issuer(3, "sign raw tx validated by issuer");
int ID;
String mDescription;
SigningMethod(int ID, String description) {
this.ID = ID;
mDescription = description;
}
static SigningMethod FindByID(int ID) {
SigningMethod[] methods = values();
for (SigningMethod m : methods) {
if (m.ID == ID) return m;
}
return SigningMethod.Sign_Hash;
}
public String getDescription() {
return mDescription;
}
}
private SigningMethod signingMethod;
public void setSigningMethod(int signingMethodID) {
this.signingMethod = SigningMethod.FindByID(signingMethodID);
}
public SigningMethod getSigningMethod() {
return signingMethod;
}
public TangemCard(String UID) {
this.UID = UID;
}
public enum Status {
NotPersonalized(0), Empty(1), Loaded(2), Purged(3);
Status(int Code) {
mCode = Code;
}
private int mCode;
public int getCode() {
return mCode;
}
public static Status fromCode(int code) {
for (Status s : values()) {
if (s.getCode() == code) return s;
}
return null;
}
}
public void setStatus(Status status) {
this.status = status;
}
public Status getStatus() {
return status;
}
public byte[] getCID() {
return CID;
}
public void setCID(byte[] value) {
this.CID = value;
}
public String getCIDDescription() {
String strCID = Util.bytesToHex(CID);
try {
return strCID.substring(0, 4) + " " + strCID.substring(4, 8) + " " + strCID.substring(8, 12) + " " + strCID.substring(12, 16);
} catch (Exception e) {
return strCID;
}
}
public void setWallet(String wallet) {
this.wallet = wallet;
}
public String getWallet() {
return wallet;
}
public String getShortWalletString() {
if (wallet.length() < 22) {
return wallet;
} else {
return wallet.substring(0, 10) + "......" + wallet.substring(wallet.length() - 10, wallet.length());
}
}
public String getUID() {
return UID;
}
public void setUID(String UID) {
this.UID = UID;
}
private byte[] offlineBalance;
public void setOfflineBalance(byte[] offlineBalance) {
this.offlineBalance = offlineBalance;
}
public byte[] getOfflineBalance() {
return offlineBalance;
}
public void clearOfflineBalance() {
offlineBalance = null;
}
private byte[] Denomination;
private String DenominationText;
public void setDenomination(byte[] denomination, String denominationText) {
this.Denomination = denomination;
this.DenominationText = denominationText;
}
public void setDenomination(byte[] denomination) {
this.Denomination = denomination;
try {
CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain());
CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination);
CoinEngine.Amount amount=engine.convertToAmount(internalAmount);
this.DenominationText = amount.toString();
} catch (Exception e) {
e.printStackTrace();
this.DenominationText = "N/A";
}
}
public byte[] getDenomination() {
return Denomination;
}
public int SignedHashes = -1; // Will remain -1 if tag was not found on card (= not safe to accept)
public void setSignedHashes(int SignedHashes) {
this.SignedHashes = SignedHashes;
}
public int getSignedHashes() {
return SignedHashes;
}
public String getDenominationText() {
return DenominationText;
}
public void setDenominationText(String denominationText) {
DenominationText = denominationText;
}
public void clearDenomination() {
Denomination = null;
DenominationText = null;
}
public Bundle getAsBundle() {
Bundle B = new Bundle();
saveToBundle(B);
return B;
}
public void saveToBundle(Bundle B) {
try {
B.putString("UID", UID);
B.putByteArray("CID", CID);
B.putString("PIN", PIN);
B.putString("PIN2", PIN2.name());
B.putString("Status", status.name());
B.putString("Blockchain", blockchainID);
B.putString("BlockchainName", blockchainName);
B.putInt("TokensDecimal", tokensDecimal);
B.putString("TokenSymbol", tokenSymbol);
B.putString("ContractAddress", contractAddress);
if (dtPersonalization != null) B.putLong("dtPersonalization", dtPersonalization.getTime());
B.putInt("RemainingSignatures", remainingSignatures);
B.putInt("MaxSignatures", maxSignatures);
B.putInt("Health", health);
if (settingsMask != null) B.putInt("settingsMask", settingsMask);
B.putInt("pauseBeforePIN2", pauseBeforePIN2);
if (signingMethod != null) B.putString("signingMethod", signingMethod.name());
if (manufacturer != null) B.putString("Manufacturer", manufacturer.name());
if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name());
if (issuer != null) B.putString("Issuer", issuer.getID());
if (issuerPublicDataKey != null) B.putByteArray("IssuerPublicDataKey", issuerPublicDataKey);
if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion);
if (batch != null) B.putString("Batch", batch);
B.putBoolean("ManufacturerConfirmed", manufacturerConfirmed);
B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid());
B.putByteArray("CardPublicKey", getCardPublicKey());
B.putInt("SignedHashes", getSignedHashes());
B.putString("Wallet", wallet);
B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid());
if (pbWalletKey != null)
B.putByteArray("PublicKey", pbWalletKey);
if (pbWalletKeyRar != null)
B.putByteArray("PublicKeyRar", pbWalletKeyRar);
if (getOfflineBalance() != null) B.putByteArray("OfflineBalance", getOfflineBalance());
if (getDenomination() != null) B.putByteArray("Denomination", getDenomination());
if (getIssuerData() != null && getIssuerDataSignature() != null) {
B.putByteArray("IssuerData", getIssuerData());
B.putByteArray("IssuerDataSignature", getIssuerDataSignature());
B.putBoolean("NeedWriteIssuerData", getNeedWriteIssuerData());
}
if (codeConfirmed != null)
B.putBoolean("codeConfirmed", codeConfirmed);
if (codeConfirmed != null)
B.putBoolean("codeConfirmed", codeConfirmed);
if (onlineVerified != null)
B.putBoolean("onlineVerified", onlineVerified);
if (onlineValidated != null)
B.putBoolean("onlineValidated", onlineValidated);
if (codeConfirmed != null)
B.putBoolean("codeConfirmed", codeConfirmed);
if (codeConfirmed != null)
B.putBoolean("codeConfirmed", codeConfirmed);
if (onlineVerified != null)
B.putBoolean("onlineVerified", onlineVerified);
if (onlineValidated != null)
B.putBoolean("onlineValidated", onlineValidated);
} catch (Exception e) {
Log.e("Can't save to bundle ", e.getMessage());
}
}
public void loadFromBundle(Bundle B) {
UID = B.getString("UID");
CID = B.getByteArray("CID");
PIN = B.getString("PIN");
PIN2 = PIN2_Mode.valueOf(B.getString("PIN2"));
status = Status.valueOf(B.getString("Status"));
wallet = B.getString("Wallet");
blockchainID = B.getString("Blockchain");
tokensDecimal = B.getInt("TokensDecimal", 18);
tokenSymbol = B.getString("TokenSymbol", "");
contractAddress = B.getString("ContractAddress", "");
if (B.containsKey("BlockchainName"))
blockchainName = B.getString("BlockchainName", "");
if (B.containsKey("dtPersonalization")) {
dtPersonalization = new Date(B.getLong("dtPersonalization"));
}
remainingSignatures = B.getInt("RemainingSignatures");
maxSignatures = B.getInt("MaxSignatures");
health = B.getInt("health");
if (B.containsKey("settingsMask")) settingsMask = B.getInt("settingsMask");
pauseBeforePIN2 = B.getInt("pauseBeforePIN2");
if (B.containsKey("signingMethod"))
signingMethod = SigningMethod.valueOf(B.getString("signingMethod"));
if (B.containsKey("Manufacturer"))
manufacturer = Manufacturer.valueOf(B.getString("Manufacturer"));
manufacturerConfirmed = B.getBoolean("ManufacturerConfirmed");
if (B.containsKey("EncryptionMode"))
encryptionMode = EncryptionMode.valueOf(B.getString("EncryptionMode"));
else
encryptionMode = null;
if (B.containsKey("SignedHashes")) setSignedHashes(B.getInt("SignedHashes"));
if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer"));
if (B.containsKey("IssuerPublicDataKey"))
issuerPublicDataKey = B.getByteArray("IssuerPublicDataKey");
if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion");
if (B.containsKey("Batch")) batch = B.getString("Batch");
cardPublicKeyValid = B.getBoolean("CardPublicKeyValid");
if (B.containsKey("CardPublicKey")) setCardPublicKey(B.getByteArray("CardPublicKey"));
if (B.containsKey("OfflineBalance")) setOfflineBalance(B.getByteArray("OfflineBalance"));
else clearOfflineBalance();
if (B.containsKey("Denomination")) setDenomination(B.getByteArray("Denomination"));
else clearDenomination();
if (B.containsKey("IssuerData") && B.containsKey("IssuerDataSignature"))
setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature"));
else setIssuerData(null, null);
if (B.containsKey("NeedWriteIssuerData"))
setNeedWriteIssuerData(B.getBoolean("NeedWriteIssuerData"));
walletPublicKeyValid = B.getBoolean("WalletPublicKeyValid");
if (B.containsKey("PublicKey")) {
pbWalletKey = B.getByteArray("PublicKey");
}
if (B.containsKey("PublicKeyRar")) {
pbWalletKeyRar = B.getByteArray("PublicKeyRar");
}
if (B.containsKey("codeConfirmed"))
codeConfirmed = B.getBoolean("codeConfirmed");
if (B.containsKey("onlineVerified"))
onlineVerified = B.getBoolean("onlineVerified");
if (B.containsKey("onlineValidated"))
onlineValidated = B.getBoolean("onlineValidated");
}
public enum EncryptionMode {
None((byte) 0x0), Fast((byte) 0x1), Strong((byte) 0x2);
private byte P;
EncryptionMode(byte P) {
this.P = P;
}
public int getP() {
return P;
}
}
public EncryptionMode encryptionMode = EncryptionMode.None;
}

View file

@ -5,6 +5,8 @@ import android.content.Intent;
import android.os.Bundle;
import com.tangem.Constant;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.tangemcard.data.TangemCard;
public class TangemContext {
@ -121,4 +123,17 @@ public class TangemContext {
if (context != null) return getContext().getResources().getString(stringId);
return "context.resources.string[" + stringId + "]";
}
public void setDenomination(byte[] denomination) {
try {
CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain());
CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination);
CoinEngine.Amount amount=engine.convertToAmount(internalAmount);
card.setDenomination(denomination,amount.toString());
} catch (Exception e) {
e.printStackTrace();
card.setDenomination(denomination,"N/A");
}
}
}

View file

@ -3,16 +3,16 @@ package com.tangem.domain.wallet.bch;
import android.net.Uri;
import android.text.InputFilter;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.TLV;
import com.tangem.tangemcard.data.PINStorage;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.domain.wallet.BalanceValidator;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.domain.wallet.CoinEngineFactory;
import com.tangem.domain.wallet.btc.BtcData;
import com.tangem.domain.wallet.CoinData;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.Transaction;
import com.tangem.domain.wallet.UnspentOutputInfo;
@ -20,7 +20,7 @@ import com.tangem.domain.wallet.BTCUtils;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import com.tangem.wallet.R;
import java.io.ByteArrayOutputStream;

View file

@ -3,15 +3,15 @@ package com.tangem.domain.wallet.btc;
import android.net.Uri;
import android.text.InputFilter;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.TLV;
import com.tangem.tangemcard.data.PINStorage;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.domain.wallet.BalanceValidator;
import com.tangem.domain.wallet.Base58;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.domain.wallet.CoinData;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.Transaction;
import com.tangem.domain.wallet.UnspentOutputInfo;
@ -19,7 +19,7 @@ import com.tangem.domain.wallet.BTCUtils;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil;
import com.tangem.util.Util;
import com.tangem.tangemcard.util.Util;
import com.tangem.wallet.R;
import java.io.ByteArrayOutputStream;

View file

@ -4,18 +4,18 @@ import android.net.Uri;
import android.text.InputFilter;
import android.util.Log;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.TLV;
import com.tangem.tangemcard.data.PINStorage;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.domain.wallet.BalanceValidator;
import com.tangem.domain.wallet.Blockchain;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.domain.wallet.CoinData;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.ECDSASignatureETH;
import com.tangem.domain.wallet.EthTransaction;
import com.tangem.domain.wallet.Issuer;
import com.tangem.tangemcard.data.Issuer;
import com.tangem.domain.wallet.Keccak256;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.BTCUtils;
import com.tangem.util.CryptoUtil;

View file

@ -5,17 +5,17 @@ import android.text.InputFilter;
import android.util.Log;
import com.google.common.base.Strings;
import com.tangem.data.db.PINStorage;
import com.tangem.domain.cardReader.CardProtocol;
import com.tangem.domain.cardReader.TLV;
import com.tangem.tangemcard.data.PINStorage;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.TLV;
import com.tangem.domain.wallet.BalanceValidator;
import com.tangem.domain.wallet.CoinData;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.ECDSASignatureETH;
import com.tangem.domain.wallet.EthTransaction;
import com.tangem.domain.wallet.Issuer;
import com.tangem.tangemcard.data.Issuer;
import com.tangem.domain.wallet.Keccak256;
import com.tangem.domain.wallet.TangemCard;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.BTCUtils;
import com.tangem.util.CryptoUtil;

View file

@ -18,13 +18,15 @@ import com.tangem.data.network.ServerApiCommon
import com.tangem.data.network.ServerApiElectrum
import com.tangem.data.network.ServerApiInfura
import com.tangem.data.network.model.InfuraResponse
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.domain.wallet.btc.BtcData
import com.tangem.tangemcard.data.Blockchain
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.util.Util
import com.tangem.util.*
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_confirm_payment.*
import org.json.JSONException
import java.io.IOException
import java.math.BigDecimal
import java.math.BigInteger

View file

@ -12,14 +12,13 @@ import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.data.nfc.CreateNewWalletTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.tasks.CreateNewWalletTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_create_new_wallet.*

View file

@ -13,14 +13,14 @@ import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.view.View
import android.widget.Toast
import com.tangem.data.nfc.VerifyCardTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.tasks.VerifyCardTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.TangemCard
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_empty_wallet.*

View file

@ -26,18 +26,19 @@ import android.widget.Toast
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.data.Logger
import com.tangem.data.db.PINStorage
import com.tangem.tangemcard.data.PINStorage
import com.tangem.data.network.ServerApiCommon
import com.tangem.data.nfc.DeviceNFCAntennaLocation
import com.tangem.data.nfc.ReadCardInfoTask
import com.tangem.tangemcard.data.DeviceNFCAntennaLocation
import com.tangem.tangemcard.tasks.ReadCardInfoTask
import com.tangem.di.Navigator
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.Firmwares
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.data.Firmwares
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.RootFoundDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.data.Issuer
import com.tangem.tangemcard.data.TangemCard
import com.tangem.util.CommonUtil
import com.tangem.util.PhoneUtility
import com.tangem.wallet.BuildConfig

View file

@ -20,10 +20,10 @@ import android.util.Log
import android.view.View
import android.widget.Button
import com.tangem.data.fingerprint.StartFingerprintReaderTask
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.data.fingerprint.FingerprintHelper
import com.tangem.data.db.PINStorage
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.data.PINStorage
import com.tangem.tangemcard.data.TangemCard
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_request.*
import kotlinx.android.synthetic.main.layout_pin_buttons.*

View file

@ -21,7 +21,7 @@ import android.widget.Button
import android.widget.Toast
import com.tangem.data.fingerprint.ConfirmWithFingerprintTask
import com.tangem.data.fingerprint.FingerprintHelper
import com.tangem.data.db.PINStorage
import com.tangem.tangemcard.data.PINStorage
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_save.*
import kotlinx.android.synthetic.main.layout_pin_buttons.*

View file

@ -12,13 +12,13 @@ import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.data.nfc.SwapPINTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.tasks.SwapPINTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.TangemCard
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_swap.*

View file

@ -10,8 +10,8 @@ import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import com.tangem.data.network.CryptonitOtherApi
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.Blockchain
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.Blockchain
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.R

View file

@ -13,8 +13,8 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import com.tangem.data.network.Cryptonit
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.Blockchain
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.Blockchain
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.util.DecimalDigitsInputFilter

View file

@ -16,8 +16,8 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import com.tangem.data.network.Kraken
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.Blockchain
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.Blockchain
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.R

View file

@ -12,8 +12,8 @@ import android.text.Html
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.Blockchain
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.data.Blockchain
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.R

View file

@ -12,14 +12,13 @@ import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Toast
import com.tangem.data.nfc.PurgeTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.tasks.PurgeTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_purge.*

View file

@ -11,9 +11,11 @@ import com.tangem.data.network.ElectrumRequest
import com.tangem.data.network.ServerApiElectrum
import com.tangem.data.network.ServerApiInfura
import com.tangem.data.network.model.InfuraResponse
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.domain.wallet.eth.EthData
import com.tangem.tangemcard.data.Blockchain
import com.tangem.tangemcard.data.TangemCard
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import java.io.IOException

View file

@ -13,14 +13,14 @@ import android.view.KeyEvent
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.data.nfc.SignPaymentTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.tasks.SignPaymentTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_sign_payment.*

View file

@ -8,7 +8,7 @@ import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.domain.wallet.CoinData
import com.tangem.domain.wallet.TangemCard
import com.tangem.tangemcard.data.TangemCard
import com.tangem.presentation.fragment.VerifyCard
import com.tangem.wallet.R
import javax.inject.Inject

View file

@ -19,17 +19,17 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.tangem.data.db.LocalStorage
import com.tangem.data.db.PINStorage
import com.tangem.tangemcard.data.LocalStorage
import com.tangem.tangemcard.data.PINStorage
import com.tangem.data.network.ElectrumRequest
import com.tangem.data.network.ServerApiCommon
import com.tangem.data.network.ServerApiElectrum
import com.tangem.data.network.ServerApiInfura
import com.tangem.data.network.model.CardVerifyAndGetInfo
import com.tangem.data.network.model.InfuraResponse
import com.tangem.data.nfc.VerifyCardTask
import com.tangem.domain.cardReader.CardProtocol
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.tasks.VerifyCardTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.domain.wallet.btc.BtcData
@ -41,7 +41,9 @@ import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.PINSwapWarningDialog
import com.tangem.presentation.dialog.ShowQRCodeDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.util.Util
import com.tangem.tangemcard.data.Blockchain
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.util.Util
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fr_loaded_wallet.*

View file

@ -13,14 +13,16 @@ import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import android.widget.Toast
import com.tangem.data.db.PINStorage
import com.tangem.domain.cardReader.NfcManager
import com.tangem.tangemcard.data.PINStorage
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.presentation.activity.CreateNewWalletActivity
import com.tangem.presentation.activity.PurgeActivity
import com.tangem.presentation.activity.PinRequestActivity
import com.tangem.presentation.activity.PinSwapActivity
import com.tangem.presentation.dialog.PINSwapWarningDialog
import com.tangem.tangemcard.data.Blockchain
import com.tangem.tangemcard.data.TangemCard
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fr_verify_card.*

View file

@ -3,6 +3,7 @@ package com.tangem.util;
import android.util.Log;
import com.tangem.domain.wallet.ECDSASignatureETH;
import com.tangem.tangemcard.util.Util;
import org.spongycastle.asn1.ASN1EncodableVector;
import org.spongycastle.asn1.ASN1Integer;

View file

@ -1,958 +0,0 @@
package com.tangem.util;
import android.text.format.DateUtils;
import org.spongycastle.crypto.digests.RIPEMD160Digest;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;
public class Util {
public static String getSpaces(int length) {
StringBuilder buf = new StringBuilder(length);
for (int i = 0; i < length; i++) {
buf.append(" ");
}
return buf.toString();
}
public static String prettyPrintHex(String in, int indent, boolean wrapLines) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
buf.append(c);
int nextPos = i+1;
if (wrapLines && nextPos % 32 == 0 && nextPos != in.length()) {
buf.append("\n").append(getSpaces(indent));
} else if (nextPos % 2 == 0 && nextPos != in.length()) {
//buf.append(" ");
}
}
return buf.toString();
}
public static String prettyPrintHex(String in, int indent){
return prettyPrintHex(in, indent, true);
}
public static String prettyPrintHex(byte[] data, int indent) {
return Util.prettyPrintHex(Util.byteArrayToHexString(data), indent, true);
}
public static String prettyPrintHex(byte[] data) {
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, true);
}
public static String prettyPrintHex(byte[] data, int startPos, int length) {
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, true);
}
public static String prettyPrintHexNoWrap(byte[] data) {
return Util.prettyPrintHex(Util.byteArrayToHexString(data), 0, false);
}
public static String prettyPrintHexNoWrap(byte[] data, int startPos, int length) {
return Util.prettyPrintHex(Util.byteArrayToHexString(data, startPos, length), 0, false);
}
public static String prettyPrintHexNoWrap(String in) {
return Util.prettyPrintHex(in, 0, false);
}
public static String prettyPrintHex(String in) {
return prettyPrintHex(in, 0, true);
}
public static String prettyPrintHex(BigInteger bi) {
byte[] data = bi.toByteArray();
if (data[0] == (byte) 0x00) {
byte[] tmp = new byte[data.length - 1];
System.arraycopy(data, 1, tmp, 0, data.length - 1);
data = tmp;
}
return prettyPrintHex(data);
}
public static byte[] performRSA(byte[] dataBytes, byte[] expBytes, byte[] modBytes) {
int inBytesLength = dataBytes.length;
if (expBytes[0] >= (byte) 0x80) {
//Prepend 0x00 to modulus
byte[] tmp = new byte[expBytes.length + 1];
tmp[0] = (byte) 0x00;
System.arraycopy(expBytes, 0, tmp, 1, expBytes.length);
expBytes = tmp;
}
if (modBytes[0] >= (byte) 0x80) {
//Prepend 0x00 to modulus
byte[] tmp = new byte[modBytes.length + 1];
tmp[0] = (byte) 0x00;
System.arraycopy(modBytes, 0, tmp, 1, modBytes.length);
modBytes = tmp;
}
if (dataBytes[0] >= (byte) 0x80) {
//Prepend 0x00 to signed data to avoid that the most significant bit is interpreted as the "signed" bit
byte[] tmp = new byte[dataBytes.length + 1];
tmp[0] = (byte) 0x00;
System.arraycopy(dataBytes, 0, tmp, 1, dataBytes.length);
dataBytes = tmp;
}
BigInteger exp = new BigInteger(expBytes);
BigInteger mod = new BigInteger(modBytes);
BigInteger data = new BigInteger(dataBytes);
byte[] result = data.modPow(exp, mod).toByteArray();
if (result.length == (inBytesLength+1) && result[0] == (byte)0x00) {
//Remove 0x00 from beginning of array
byte[] tmp = new byte[inBytesLength];
System.arraycopy(result, 1, tmp, 0, inBytesLength);
result = tmp;
}
return result;
}
public static byte[] calculateSHA1(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
return sha1.digest(data);
}
public static byte[] calculateSHA224(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-224");
return sha.digest(data);
}
public static byte[] calculateSHA256(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
return sha256.digest(data);
}
public static byte[] calculateSHA384(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-384");
return sha.digest(data);
}
public static byte[] calculateSHA512(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-512");
return sha.digest(data);
}
public static byte[] calculateSHA256(String Message) throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte data[]=Message.getBytes(Charset.forName("UTF-8"));
return sha256.digest(data);
}
public static byte[] calculateRIPEMD160(byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException {
//MessageDigest hashAlg = MessageDigest.getInstance("RIPEMD-160", "SC");
//return hashAlg.digest(data);
RIPEMD160Digest digest = new RIPEMD160Digest();
digest.update(data, 0, data.length);
byte[] out = new byte[20];
digest.doFinal(out, 0);
return out;
}
public static String byte2Hex(byte b) {
String[] HEX_DIGITS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
int nb = b & 0xFF;
int i_1 = (nb >>> 4) & 0xF;
int i_2 = nb & 0xF;
return HEX_DIGITS[i_1] + HEX_DIGITS[i_2];
}
public static String short2Hex(short s) {
byte b1 = (byte) (s >>> 8);
byte b2 = (byte) (s & 0xFF);
return byte2Hex(b1) + byte2Hex(b2);
}
public static int byteToInt(byte b) {
return (int) b & 0xFF;
}
public static int byteToInt(byte first, byte second) {
int value = (first & 0xFF) << 8;
value += second & 0xFF;
return value;
}
public static short byte2Short(byte b1, byte b2) {
return (short) ((b1 << 8) | (b2 & 0xFF));
}
public static String getFormattedNanoTime(long nano) {
StringBuilder buf = new StringBuilder();
buf.append((int) (nano / 1000000));
buf.append("ms ");
buf.append(nano % 1000000);
buf.append("ns");
return buf.toString();
}
public static String formatDate(Date date)
{
return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR);
// return DateFormat.getDateInstance(DateFormat.SHORT).format(date);
}
public static String formatDateTime(Date date)
{
return formatDate(date)+" "+formatTime(date);
}
public static String formatTime(Date date)
{
return new SimpleDateFormat("HH:mm:ss").format(date);
// DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date)
// return DateUtils.formatDateTime(null, date.getTime(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR);//DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date);
}
public static byte[] getCurrentDateAsNumericEncodedByteArray(){
SimpleDateFormat format = new SimpleDateFormat("yyMMdd", Locale.US);
return fromHexString(format.format(new Date()));
}
//This prints all non-control characters common to all parts of ISO/IEC 8859
//See EMV book 4 Annex B: Table 36: VolleyHelper Character Set
public static String getSafePrintChars(byte[] byteArray) {
if (byteArray == null) {
return "";
// throw new IllegalArgumentException("Argument 'byteArray' cannot be null");
}
return getSafePrintChars(byteArray, 0, byteArray.length);
}
public static String getSafePrintChars(byte[] byteArray, int startPos, int length) {
if (byteArray == null) {
return "";
// throw new IllegalArgumentException("Argument 'byteArray' cannot be null");
}
if(byteArray.length < startPos+length){
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
}
StringBuilder buf = new StringBuilder();
for (int i = startPos; i < startPos+length; i++) {
if (byteArray[i] >= (byte) 0x20 && byteArray[i] < (byte) 0x7F) {
buf.append((char) byteArray[i]);
} else {
buf.append(".");
}
}
return buf.toString();
}
public static byte[] hexToBytes(String str) {
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(str.substring(2 * i, 2 * i + 2),
16);
}
return bytes;
}
final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
if( bytes==null ) return "[EMPTY]";
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
/**
* Converts a byte array into a hex string.
* @param byteArray the byte array source
* @return a hex string representing the byte array
*/
public static String byteArrayToHexString(final byte[] byteArray) {
if (byteArray == null) {
return "";
}
return byteArrayToHexString(byteArray, 0, byteArray.length);
}
public static String byteArrayToHexString(final byte[] byteArray, int startPos, int length) {
if (byteArray == null) {
return "";
}
if(byteArray.length < startPos+length){
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+byteArray.length+")");
}
// int readBytes = byteArray.length;
StringBuilder hexData = new StringBuilder();
int onebyte;
for (int i = 0; i < length; i++) {
onebyte = ((0x000000ff & byteArray[startPos+i]) | 0xffffff00);
hexData.append(Integer.toHexString(onebyte).substring(6));
}
return hexData.toString();
}
public static String int2Hex(int i) {
String hex = Integer.toHexString(i);
if (hex.length() % 2 != 0) {
hex = "0" + hex;
}
return hex;
}
public static String int2HexZeroPad(int i) {
String hex = int2Hex(i);
if (hex.length() % 2 != 0) {
hex = "0" + hex;
}
return hex;
}
/**
* The length of the returned array depends on the size of the int
* @param value
* @return
*/
public static byte[] intToByteArray(int value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte one = (byte) (value >>> 24);
byte two = (byte) (value >>> 16);
byte three = (byte) (value >>> 8);
byte four = (byte) (value);
boolean found = false;
if (one > 0x00) {
baos.write(one);
found = true;
}
if (found || two > 0x00) {
baos.write(two);
found = true;
}
if (found || three > 0x00) {
baos.write(three);
}
baos.write(four);
return baos.toByteArray();
}
/**
* Returns a byte array with length = 2
* @param value
* @return
*/
public static byte[] intToByteArray2(int value) {
return new byte[]{
(byte) (value >>> 8),
(byte) value};
}
/**
* Returns a byte array with length = 4
* @param value
* @return
*/
public static byte[] intToByteArray4(int value) {
return new byte[]{
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) value};
}
public static byte[] longToByteArray8(long value) {
return new byte[]{
(byte) (value >>> 56),
(byte) (value >>> 48),
(byte) (value >>> 40),
(byte) (value >>> 32),
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) value};
}
public static int byteArrayToInt(byte[] byteArray) throws IllegalArgumentException {
if( byteArray.length==1 ) return byteArray[0]&0xFF;
java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray);
switch (byteArray.length)
{
case 2: return BB.getShort();
case 4: return BB.getInt();
default: throw new IllegalArgumentException("Length must be 1,2 or 4. Length = " + byteArray.length);
}
}
public static long byteArrayToLong(byte[] byteArray) throws IllegalArgumentException {
if( byteArray.length==1 ) return byteArray[0]&0xFF;
java.nio.ByteBuffer BB=java.nio.ByteBuffer.wrap(byteArray);
switch (byteArray.length)
{
case 2: return BB.getShort();
case 4: return BB.getInt();
case 8: return BB.getLong();
default: throw new IllegalArgumentException("Length must be 1,2,4 or 8. Length = " + byteArray.length);
}
}
public static byte[] longToByteArray(long value)
{
return new byte[]{
(byte) (value >>> 56),
(byte) (value >>> 48),
(byte) (value >>> 40),
(byte) (value >>> 32),
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) value};
}
public static int byteArrayToInt(byte[] byteArray, int startPos, int length) throws IllegalArgumentException {
if (byteArray == null) {
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
}
if (length <= 0 || length > 4) {
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
}
if (length == 4 && Util.isBitSet(byteArray[startPos], 8)){
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
}
int value = 0;
for (int i = 0; i < length; i++) {
value += ((byteArray[startPos+i] & 0xFF) << 8 * (length - i - 1));
}
return value;
}
public static long byteArrayToLong(byte[] byteArray, int startPos, int length) throws IllegalArgumentException {
if (byteArray == null) {
throw new IllegalArgumentException("Parameter 'byteArray' cannot be null");
}
if (length <= 0 || length > 8) {
throw new IllegalArgumentException("Length must be between 1 and 4. Length = " + length);
}
if (length == 8 && Util.isBitSet(byteArray[startPos], 8)){
throw new IllegalArgumentException("Signed bit is set (leftmost bit): " + Util.byte2Hex(byteArray[startPos]));
}
long value = 0;
for (int i = 0; i < length; i++) {
value += ((byteArray[startPos+i] & (long)0xFF) << 8 * (length - i - 1));
}
return value;
}
public static byte[] fromHexString(String encoded) {
encoded = removeSpaces(encoded);
if (encoded.length() == 0){
return new byte[0];
}
if ((encoded.length() % 2) != 0) {
throw new IllegalArgumentException("Input string must contain an even number of characters: "+encoded);
}
final byte result[] = new byte[encoded.length() / 2];
final char enc[] = encoded.toCharArray();
for (int i = 0; i < enc.length; i += 2) {
StringBuilder curr = new StringBuilder(2);
curr.append(enc[i]).append(enc[i + 1]);
result[i / 2] = (byte) Integer.parseInt(curr.toString(), 16);
}
return result;
}
public static String removeCRLFTab(String s) {
StringTokenizer st = new StringTokenizer(s, "\r\n\t", false);
StringBuilder buf = new StringBuilder();
while (st.hasMoreElements()) {
buf.append(st.nextElement());
}
return buf.toString();
}
public static String removeSpaces(String s) {
return s.replaceAll(" ", "");
}
public static String readInputStreamToString(InputStream is, String encoding) throws IOException {
InputStreamReader input = new InputStreamReader(is, encoding);
final int CHARS_PER_PAGE = 5000; //counting spaces
final char[] buffer = new char[CHARS_PER_PAGE];
StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
for (int read = input.read(buffer, 0, buffer.length);
read != -1;
read = input.read(buffer, 0, buffer.length)) {
output.append(buffer, 0, read);
}
String text = output.toString();
return text;
}
public static void writeStringToFile(String string, String fileName, boolean append) throws IOException {
BufferedWriter out = new BufferedWriter(new FileWriter(fileName, append));
out.write(string);
out.close();
}
/**
* Binary Coded Decimal (BCD)
* @param val
* @return
*/
public static byte[] intToBinaryEncodedDecimalByteArray(int val){
String str = String.valueOf(val);
if(str.length() % 2 != 0){
str = "0"+str;
}
return Util.fromHexString(str);
}
/**
* This method converts the literal hex representation of a byte to an int.
* eg 0x70 = 70 (int)
* @param b
*/
public static int binaryCodedDecimalToInt(byte b) {
String hex = Util.byte2Hex(b);
try {
return Integer.parseInt(hex);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("The hex representation of argument b must be digits", ex);
}
}
/**
* This method converts the literal hex representation of a decimal
* encoded in 1-5 bytes to an int.
* The value should not be larger than Integer.MAX_VALUE
*
* eg 0x70 = 70 (decimal)
* eg 0x21 47 48 36 47 = 2147483647 (decimal)
* @param hex
*/
public static int binaryHexCodedDecimalToInt(String hex) {
if (hex == null) {
throw new IllegalArgumentException("Param hex cannot be null");
}
hex = Util.removeSpaces(hex);
if (hex.length() > 10) {
throw new IllegalArgumentException("There must be a maximum of 5 hex octets. hex=" + hex);
}
try {
return Integer.parseInt(hex);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Argument hex must be all digits. hex="+hex, ex);
}
}
/**
* This method converts a 1-5 byte BCD to an int.
* eg 0x7099 = 7099 (int)
* @param bcdArray
*/
public static int binaryHexCodedDecimalToInt(byte[] bcdArray) {
if (bcdArray == null) {
throw new IllegalArgumentException("Param bcdArray cannot be null");
}
return binaryHexCodedDecimalToInt(Util.byteArrayToHexString(bcdArray));
}
/**
* This returns a String with length = 8
* @param val
* @return
*/
public static String byte2BinaryLiteral(byte val) {
String s = Integer.toBinaryString(Util.byteToInt(val));
if (s.length() < 8) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8 - s.length(); i++) {
sb.append('0');
}
sb.append(s);
s = sb.toString();
}
return s;
}
/**
* Returns a bitset containing the values in bytes.
* The byte-ordering of bytes must be big-endian which means the most significant bit is in element 0.
*
* @param bytes
* @return
*/
public static BitSet byteArray2BitSet(byte[] bytes) {
BitSet bits = new BitSet();
for (int i = 0; i < bytes.length * 8; i++) {
if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
bits.set(i);
}
}
return bits;
}
/* Returns a byte array of at least length 1.
* The most significant bit in the result is guaranteed not to be a 1
* (since BitSet does not support sign extension).
* The byte-ordering of the result is big-endian which means the most significant bit is in element 0.
* The bit at index 0 of the bit set is assumed to be the least significant bit.
*/
public static byte[] bitSet2ByteArray(BitSet bits) {
byte[] bytes = new byte[bits.length() / 8 + 1];
for (int i = 0; i < bits.length(); i++) {
if (bits.get(i)) {
bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
}
}
return bytes;
}
/**
*
* @param val
* @param bitPos The leftmost bit is 8 (the most significant bit)
* @return
*/
public static boolean isBitSet(byte val, int bitPos) {
if (bitPos < 1 || bitPos > 8) {
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
}
if ((val >>> (bitPos - 1) & 0x1) == 1) {
return true;
}
return false;
}
// /**
// *
// * @param val
// * @return
// */
// public static int getBitsSetCount(byte val) {
// int numBitsSet = 0;
// for(int i=1; i<=8; i++){
// if(Util.isBitSet(val, i)){
// numBitsSet++;
// }
// }
// return numBitsSet;
// }
/**
*
* @param data
* @param bitPos The leftmost bit is 8
* @param on
* @return
*/
public static byte setBit(byte data, int bitPos, boolean on) {
if (bitPos < 1 || bitPos > 8) {
throw new IllegalArgumentException("parameter 'bitPos' must be between 1 and 8. bitPos=" + bitPos);
}
if (on) {
// set bit
return data |= 1 << (bitPos - 1);
} else {
// clear bit
return data &= ~(1 << (bitPos - 1));
}
}
public static byte[] generateRandomBytes(int numBytes) {
// TODO: get bytes from a hardware RNG, or set seed
byte[] rndBytes = new byte[numBytes];
SecureRandom random = new SecureRandom();
random.nextBytes(rndBytes);
return rndBytes;
}
public static byte generateRandomByte() {
SecureRandom random = new SecureRandom();
return (byte)(random.nextInt()&0xFF);
}
public static InputStream loadResource(Class<?> cls, String path){
return cls.getResourceAsStream(path);
}
/**
* Copies the specified array, prepending 0x00, or cutting off MSBytes if necessary
* @param original
* @param newLength
* @return
*/
public static byte[] resizeArray(byte[] original, int newLength) {
if(original == null){
throw new IllegalArgumentException("byte array cannot be null");
}
if(newLength < 0){
throw new IllegalArgumentException("Illegal new length: "+newLength+". Must be >= 0");
}
if(newLength == 0){
return new byte[0];
}
byte[] tmp = new byte[newLength];
int srcPos = tmp.length > original.length ? 0 : original.length - tmp.length;
int destPos = tmp.length > original.length ? tmp.length - original.length : 0;
int length = tmp.length > original.length ? original.length : tmp.length;
System.arraycopy(original, srcPos, tmp, destPos, length);
return tmp;
}
public static byte[] copyByteArray(byte[] array2Copy){
// byte[] copy = new byte[array2Copy.length];
// System.arraycopy(array2Copy, 0, copy, 0, array2Copy.length);
// return copy;
if (array2Copy == null) {
//return new byte[0] instead?
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
}
return copyByteArray(array2Copy, 0, array2Copy.length);
}
public static byte[] copyByteArray(byte[] array2Copy, int startPos, int length){
if (array2Copy == null) {
//return new byte[0] instead?
throw new IllegalArgumentException("Argument 'array2Copy' cannot be null");
}
if(array2Copy.length < startPos+length){
throw new IllegalArgumentException("startPos("+startPos+")+length("+length+") > byteArray.length("+array2Copy.length+")");
}
byte[] copy = new byte[array2Copy.length];
System.arraycopy(array2Copy, startPos, copy, 0, length);
return copy;
}
public static String getStackTrace(Throwable t){
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
public static Class<?> getCallerClass(int i) {
Class<?>[] classContext = new SecurityManager() {
@Override public Class<?>[] getClassContext() {
return super.getClassContext();
}
}.getClassContext();
if (classContext != null) {
for (int j = 0; j < classContext.length; j++) {
if (classContext[j] == Util.class) {
return classContext[i+j];
}
}
} else {
// SecurityManager.getClassContext() returns null on Android 4.0
try {
StackTraceElement[] classNames = Thread.currentThread().getStackTrace();
for (int j = 0; j < classNames.length; j++) {
if (Class.forName(classNames[j].getClassName()) == Util.class) {
return Class.forName(classNames[i+j].getClassName());
}
}
} catch (ClassNotFoundException e) { }
}
return null;
}
public static String decodeOID(byte[] enc){
StringBuilder sb = new StringBuilder();
//First OID Component (standard)
//0: ITU-T
//1: ISO
//2: joint-iso-itu-t
//Second OID Component (part in a multi part standard)
//0: standard
//1: registration-authority
//2: member-body
//3: identified-organization
long firstSubidentifier = 0;
int i=0;
while(Util.isBitSet(enc[i], 8)){
firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f);
i++;
}
firstSubidentifier = (firstSubidentifier << 7) | (enc[i] & 0x7f);
i++;
if(firstSubidentifier >= 80){
long firstOIDComp = 2;
long secondOIDComp = firstSubidentifier - 80;
sb.append(firstOIDComp).append(".").append(secondOIDComp);
}else{
long secondOIDComp = firstSubidentifier % 40;
long firstOIDComp = (firstSubidentifier - secondOIDComp)/40;
sb.append(firstOIDComp).append(".").append(secondOIDComp);
}
for(; i<enc.length; i++){
sb.append(".");
long subIdentifier = 0;
while(Util.isBitSet(enc[i], 8)){
subIdentifier = (subIdentifier << 7) | (enc[i] & 0x7f);
i++;
}
subIdentifier = (subIdentifier << 7) | (enc[i] & 0x7f);
sb.append(subIdentifier);
}
String oid = sb.toString();
String desc = getOIDDescription(oid);
return oid + ((desc!=null && !desc.isEmpty())?" ("+desc +")":"");
}
//Simple OID registry
//See: http://www.oid-info.com/
public static String getOIDDescription(String oid){
// 1.2.840 - one of 2 US country OIDs
// 1.2.840.114283 - Global Platform
// 1.3.6.1 - the Internet OID
// 1.3.6.1.4.1 - IANA-assigned company OIDs, used for private MIBs and such things
// 1.3.6.1.4.1.42 - Sun Microsystems
// 1.3.6.1.4.1.42.2 - Sun Products
// 1.3.6.1.4.1.42.2.110 - java[XML]software
// 1.3.6.1.4.1.42.2.110.1.2 - (Unknown - Java Card?)
if(oid.startsWith("1.2.840.114283.1")){
return "Global Platform - Card Recognition Data";
}
if(oid.startsWith("1.2.840.114283.2")){
return "Global Platform v"+oid.substring(17);
}
if(oid.startsWith("1.2.840.114283.3")){
return "Global Platform - Card Identification Scheme";
}
if(oid.startsWith("1.2.840.114283.4")){
return "Global Platform SCP "+oid.substring(17, 18) + " implementation option 0x"+Util.int2Hex(Integer.parseInt(oid.substring(19)));
}
if(oid.startsWith("1.2.840.114283")){
return "Global Platform";
}
if(oid.startsWith("1.2.840")){
return "USA";
}
if(oid.startsWith("1.3.6.1.4.1.42.2.110.1.2")){
return "Sun Microsystems - Java Card ?";
}
if(oid.startsWith("1.3.6.1.4.1.42.2")){
return "Sun Microsystems - Products";
}
// if(oid.startsWith("1.3.656.840."))
//JCOP includes GP refinements according to Visa GP 2.1.1 specification.
//This tag is populated accordingly (Visa specific).
//The last number tells you what configuration it is (3: SSD + PKI, 2: PKI, 1: just symmetric crypto).
//Unfortunately this standard is not open.
return "";
}
public static void main(String[] args) {
// System.out.println(Util.isBitSet((byte) 0x5f, 2)); // 0101 1111
// System.out.println(Util.isBitSet((byte) 0x9f, 2)); // 1001 1111
//
// System.out.println(Util.byte2Short((byte) 0x6F, (byte) 0xEF));
// System.out.println(Util.short2Hex(Util.byte2Short((byte) 0x6F, (byte) 0xEF)));
//
// System.out.println(Util.byteArrayToInt(new byte[]{(byte) 0x6F, (byte) 0xEF}));
// System.out.println(Util.byteArrayToHexString(Util.intToByteArray(28655)));
//
// System.out.println(Util.byte2BinaryLiteral((byte) 0x00));
// System.out.println(Util.byte2BinaryLiteral((byte) 0x3F));
// System.out.println(Util.byte2BinaryLiteral((byte) 0x80));
// System.out.println(Util.byte2BinaryLiteral((byte) 0xAA));
// System.out.println(Util.byte2BinaryLiteral((byte) 0xFF));
//
// System.out.println(Util.byte2BinaryLiteral((byte) 0x8A));
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 5, true)));
// System.out.println(Util.byte2BinaryLiteral(Util.setBit((byte) 0x8A, 8, false)));
//
// System.out.println(Util.byteArrayToLong(Util.fromHexString("7f ff ff ff ff ff ff ff"), 0, 8));
// System.out.println(Util.byteArrayToLong(Util.fromHexString("22 18 09 04 0b 00 e0 30 23 07 00 00 00 42 d2 85 4e 23 07 00 00 00 00 21 69 42"), 13, 4));
System.out.println("1.2.840.114283.1 : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 01")));
System.out.println("1.2.840.114283.2.2.1.1 : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 02 02 01 01")));
System.out.println("1.2.840.114283.4.XXXX : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 04 02 15"))); //JCOP 31
System.out.println("1.2.840.114283.4.XXXX : " + decodeOID(Util.fromHexString("2a 86 48 86 fc 6b 04 01 05"))); //JCOP 31
System.out.println("Sun Microsystems : " + decodeOID(Util.fromHexString("2b 06 01 04 01 2a 02 6e 01 02")));
System.out.println("Unknown : " + decodeOID(Util.fromHexString("2b 85 10 86 48 64 02 01 03")));
System.out.println("{2 100 3} : " + decodeOID(Util.fromHexString("813403")));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 0)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 1)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01}, 2)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 1)));
System.out.println(Util.prettyPrintHexNoWrap(Util.resizeArray(new byte[]{0x01, 0x02}, 4)));
}
public static byte[] calculateCRC16(byte[] bytes) {
byte chBlock;
// STEP 1 Initialize the CRC-16 value
int wCRC = 0x6363; // ITU-V.41
int i = 0;
// STEP 2 Update data and Calucuate their CRC
do {
chBlock = bytes[i++];
chBlock ^= (byte) (wCRC & 0x00FF);
chBlock = (byte) (chBlock ^ (chBlock << 4));
wCRC = ((wCRC >> 8) ^ ((chBlock & 0xFF) << 8) & 0xFFFF) ^ (((chBlock & 0xFF) << 3) & 0xFFFF) ^ (((chBlock & 0xFF) >> 4) & 0xFFFF);// (wCRC>>8)^((int)chBlock<<8)^((int) chBlock<<3)^((int)chBlock>>4);
} while (i < bytes.length);
return new byte[]{(byte) (wCRC & 0xFF), (byte) ((wCRC & 0xFFFF) >> 8)};
}
public static String formatDateTimeToFileName(Date date) {
return new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss", Locale.US).format(date);
}
}